1use std::{
7 ops::Not as _,
8 panic::AssertUnwindSafe,
9 time::{Duration, Instant},
10};
11
12use cargo_metadata::PackageId;
13use crossbeam_channel::{Receiver, Sender, unbounded};
14use hir::ChangeWithProcMacros;
15use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
16use ide_db::{
17 MiniCore,
18 base_db::{Crate, ProcMacroPaths, SourceDatabase},
19};
20use itertools::Itertools;
21use load_cargo::SourceRootConfig;
22use lsp_types::{SemanticTokens, Url};
23use parking_lot::{
24 MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
25 RwLockWriteGuard,
26};
27use proc_macro_api::ProcMacroClient;
28use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
29use rustc_hash::{FxHashMap, FxHashSet};
30use stdx::thread;
31use tracing::{Level, span, trace};
32use triomphe::Arc;
33use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath};
34
35use crate::{
36 config::{Config, ConfigChange, ConfigErrors, RatomlFileKind},
37 diagnostics::{CheckFixes, DiagnosticCollection},
38 discover,
39 flycheck::{FlycheckHandle, FlycheckMessage},
40 line_index::{LineEndings, LineIndex},
41 lsp::{from_proto, to_proto::url_from_abs_path},
42 lsp_ext,
43 main_loop::Task,
44 mem_docs::MemDocs,
45 op_queue::{Cause, OpQueue},
46 reload,
47 target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec},
48 task_pool::{DeferredTaskQueue, TaskPool},
49 test_runner::{CargoTestHandle, CargoTestMessage},
50};
51
52#[derive(Debug)]
53pub(crate) struct FetchWorkspaceRequest {
54 pub(crate) path: Option<AbsPathBuf>,
55 pub(crate) force_crate_graph_reload: bool,
56}
57
58pub(crate) struct FetchWorkspaceResponse {
59 pub(crate) workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
60 pub(crate) force_crate_graph_reload: bool,
61}
62
63pub(crate) struct FetchBuildDataResponse {
64 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
65 pub(crate) build_scripts: Vec<anyhow::Result<WorkspaceBuildScripts>>,
66}
67
68pub(crate) struct Handle<H, C> {
70 pub(crate) handle: H,
71 pub(crate) receiver: C,
72}
73
74pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
75type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
76
77#[doc(alias = "GlobalMess")]
85pub(crate) struct GlobalState {
86 sender: Sender<lsp_server::Message>,
87 req_queue: ReqQueue,
88
89 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
90 pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
91 pub(crate) cancellation_pool: thread::Pool,
92
93 pub(crate) config: Arc<Config>,
94 pub(crate) config_errors: Option<ConfigErrors>,
95 pub(crate) analysis_host: AnalysisHost,
96 pub(crate) diagnostics: DiagnosticCollection,
97 pub(crate) mem_docs: MemDocs,
98 pub(crate) source_root_config: SourceRootConfig,
99 pub(crate) local_roots_parent_map: Arc<FxHashMap<SourceRootId, SourceRootId>>,
101 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
102
103 pub(crate) shutdown_requested: bool,
105 pub(crate) last_reported_status: lsp_ext::ServerStatusParams,
106
107 pub(crate) proc_macro_clients: Arc<[Option<anyhow::Result<ProcMacroClient>>]>,
109 pub(crate) build_deps_changed: bool,
110
111 pub(crate) flycheck: Arc<[FlycheckHandle]>,
113 pub(crate) flycheck_sender: Sender<FlycheckMessage>,
114 pub(crate) flycheck_receiver: Receiver<FlycheckMessage>,
115 pub(crate) last_flycheck_error: Option<String>,
116
117 pub(crate) test_run_session: Option<Vec<CargoTestHandle>>,
119 pub(crate) test_run_sender: Sender<CargoTestMessage>,
120 pub(crate) test_run_receiver: Receiver<CargoTestMessage>,
121 pub(crate) test_run_remaining_jobs: usize,
122
123 pub(crate) discover_handles: Vec<discover::DiscoverHandle>,
125 pub(crate) discover_sender: Sender<discover::DiscoverProjectMessage>,
126 pub(crate) discover_receiver: Receiver<discover::DiscoverProjectMessage>,
127 pub(crate) discover_jobs_active: u32,
128
129 pub(crate) fetch_ws_receiver: Option<(Receiver<Instant>, FetchWorkspaceRequest)>,
133
134 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
136 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
137 pub(crate) vfs_config_version: u32,
138 pub(crate) vfs_progress_config_version: u32,
139 pub(crate) vfs_done: bool,
140 pub(crate) vfs_span: Option<tracing::span::EnteredSpan>,
143 pub(crate) wants_to_switch: Option<Cause>,
144
145 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
171 pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
172 pub(crate) detached_files: FxHashSet<ManifestPath>,
173
174 pub(crate) fetch_workspaces_queue: OpQueue<FetchWorkspaceRequest, FetchWorkspaceResponse>,
176 pub(crate) fetch_build_data_queue: OpQueue<(), FetchBuildDataResponse>,
177 pub(crate) fetch_proc_macros_queue: OpQueue<(ChangeWithProcMacros, Vec<ProcMacroPaths>), bool>,
178 pub(crate) prime_caches_queue: OpQueue,
179
180 pub(crate) deferred_task_queue: DeferredTaskQueue,
190
191 pub(crate) incomplete_crate_graph: bool,
195
196 pub(crate) minicore: MiniCoreRustAnalyzerInternalOnly,
197}
198
199#[derive(Debug, Clone, Default)]
201pub(crate) struct MiniCoreRustAnalyzerInternalOnly {
202 pub(crate) minicore_text: Option<String>,
203}
204
205pub(crate) struct GlobalStateSnapshot {
207 pub(crate) config: Arc<Config>,
208 pub(crate) analysis: Analysis,
209 pub(crate) check_fixes: CheckFixes,
210 mem_docs: MemDocs,
211 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
212 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
213 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
214 pub(crate) proc_macros_loaded: bool,
218 pub(crate) flycheck: Arc<[FlycheckHandle]>,
219 minicore: MiniCoreRustAnalyzerInternalOnly,
220}
221
222impl std::panic::UnwindSafe for GlobalStateSnapshot {}
223
224impl GlobalState {
225 pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
226 let loader = {
227 let (sender, receiver) = unbounded::<vfs::loader::Message>();
228 let handle: vfs_notify::NotifyHandle = vfs::loader::Handle::spawn(sender);
229 let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
230 Handle { handle, receiver }
231 };
232
233 let task_pool = {
234 let (sender, receiver) = unbounded();
235 let handle = TaskPool::new_with_threads(sender, config.main_loop_num_threads());
236 Handle { handle, receiver }
237 };
238 let fmt_pool = {
239 let (sender, receiver) = unbounded();
240 let handle = TaskPool::new_with_threads(sender, 1);
241 Handle { handle, receiver }
242 };
243 let cancellation_pool = thread::Pool::new(1);
244
245 let deferred_task_queue = {
246 let (sender, receiver) = unbounded();
247 DeferredTaskQueue { sender, receiver }
248 };
249
250 let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
251 if let Some(capacities) = config.lru_query_capacities_config() {
252 analysis_host.update_lru_capacities(capacities);
253 }
254 let (flycheck_sender, flycheck_receiver) = unbounded();
255 let (test_run_sender, test_run_receiver) = unbounded();
256
257 let (discover_sender, discover_receiver) = unbounded();
258
259 let mut this = GlobalState {
260 sender,
261 req_queue: ReqQueue::default(),
262 task_pool,
263 fmt_pool,
264 cancellation_pool,
265 loader,
266 config: Arc::new(config.clone()),
267 analysis_host,
268 diagnostics: Default::default(),
269 mem_docs: MemDocs::default(),
270 semantic_tokens_cache: Arc::new(Default::default()),
271 shutdown_requested: false,
272 last_reported_status: lsp_ext::ServerStatusParams {
273 health: lsp_ext::Health::Ok,
274 quiescent: true,
275 message: None,
276 },
277 source_root_config: SourceRootConfig::default(),
278 local_roots_parent_map: Arc::new(FxHashMap::default()),
279 config_errors: Default::default(),
280
281 proc_macro_clients: Arc::from_iter([]),
282
283 build_deps_changed: false,
284
285 flycheck: Arc::from_iter([]),
286 flycheck_sender,
287 flycheck_receiver,
288 last_flycheck_error: None,
289
290 test_run_session: None,
291 test_run_sender,
292 test_run_receiver,
293 test_run_remaining_jobs: 0,
294
295 discover_handles: vec![],
296 discover_sender,
297 discover_receiver,
298 discover_jobs_active: 0,
299
300 fetch_ws_receiver: None,
301
302 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), Default::default()))),
303 vfs_config_version: 0,
304 vfs_progress_config_version: 0,
305 vfs_span: None,
306 vfs_done: true,
307 wants_to_switch: None,
308
309 workspaces: Arc::from(Vec::new()),
310 crate_graph_file_dependencies: FxHashSet::default(),
311 detached_files: FxHashSet::default(),
312 fetch_workspaces_queue: OpQueue::default(),
313 fetch_build_data_queue: OpQueue::default(),
314 fetch_proc_macros_queue: OpQueue::default(),
315
316 prime_caches_queue: OpQueue::default(),
317
318 deferred_task_queue,
319 incomplete_crate_graph: false,
320
321 minicore: MiniCoreRustAnalyzerInternalOnly::default(),
322 };
323 this.update_configuration(config);
325 this
326 }
327
328 pub(crate) fn process_changes(&mut self) -> bool {
329 let _p = span!(Level::INFO, "GlobalState::process_changes").entered();
330 let mut modified_ratoml_files: FxHashMap<FileId, (ChangeKind, vfs::VfsPath)> =
335 FxHashMap::default();
336
337 let mut change = ChangeWithProcMacros::default();
338 let mut guard = self.vfs.write();
339 let changed_files = guard.0.take_changes();
340 if changed_files.is_empty() {
341 return false;
342 }
343
344 let (change, modified_rust_files, workspace_structure_change) =
345 self.cancellation_pool.scoped(|s| {
346 let analysis_host = AssertUnwindSafe(&mut self.analysis_host);
349 s.spawn(thread::ThreadIntent::LatencySensitive, || {
350 { analysis_host }.0.request_cancellation()
351 });
352
353 let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
355 let vfs: &Vfs = &guard.0;
356
357 let mut workspace_structure_change = None;
358 let mut has_structure_changes = false;
360 let mut bytes = vec![];
361 let mut modified_rust_files = vec![];
362 for file in changed_files.into_values() {
363 let vfs_path = vfs.file_path(file.file_id);
364 if let Some(("rust-analyzer", Some("toml"))) = vfs_path.name_and_extension() {
365 modified_ratoml_files.insert(file.file_id, (file.kind(), vfs_path.clone()));
367 }
368
369 if let Some(path) = vfs_path.as_path() {
370 has_structure_changes |= file.is_created_or_deleted();
371
372 if file.is_modified() && path.extension() == Some("rs") {
373 modified_rust_files.push(file.file_id);
374 }
375
376 let additional_files = self
377 .config
378 .discover_workspace_config()
379 .map(|cfg| {
380 cfg.files_to_watch.iter().map(String::as_str).collect::<Vec<&str>>()
381 })
382 .unwrap_or_default();
383
384 let path = path.to_path_buf();
385 if file.is_created_or_deleted() {
386 workspace_structure_change.get_or_insert((path, false)).1 |=
387 self.crate_graph_file_dependencies.contains(vfs_path);
388 } else if reload::should_refresh_for_change(
389 &path,
390 file.kind(),
391 &additional_files,
392 ) {
393 trace!(?path, kind = ?file.kind(), "refreshing for a change");
394 workspace_structure_change.get_or_insert((path.clone(), false));
395 }
396 }
397
398 if !file.exists() {
400 self.diagnostics.clear_native_for(file.file_id);
401 }
402
403 let text = if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) =
404 file.change
405 {
406 String::from_utf8(v).ok().map(|text| {
407 let (text, line_endings) = LineEndings::normalize(text);
410 (text, line_endings)
411 })
412 } else {
413 None
414 };
415 bytes.push((file.file_id, text));
418 }
419 let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
420 bytes.into_iter().for_each(|(file_id, text)| {
421 let text = match text {
422 None => None,
423 Some((text, line_endings)) => {
424 line_endings_map.insert(file_id, line_endings);
425 Some(text)
426 }
427 };
428 change.change_file(file_id, text);
429 });
430 if has_structure_changes {
431 let roots = self.source_root_config.partition(vfs);
432 change.set_roots(roots);
433 }
434 (change, modified_rust_files, workspace_structure_change)
435 });
436
437 self.analysis_host.apply_change(change);
438 if !modified_ratoml_files.is_empty()
439 || !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
440 {
441 let config_change = {
442 let _p = span!(Level::INFO, "GlobalState::process_changes/config_change").entered();
443 let user_config_path = (|| {
444 let mut p = Config::user_config_dir_path()?;
445 p.push("rust-analyzer.toml");
446 Some(p)
447 })();
448
449 let user_config_abs_path = user_config_path.as_deref();
450
451 let mut change = ConfigChange::default();
452 let db = self.analysis_host.raw_database();
453
454 let workspace_ratoml_paths = self
457 .workspaces
458 .iter()
459 .map(|ws| {
460 VfsPath::from({
461 let mut p = ws.workspace_root().to_owned();
462 p.push("rust-analyzer.toml");
463 p
464 })
465 })
466 .collect_vec();
467
468 for (file_id, (change_kind, vfs_path)) in modified_ratoml_files {
469 tracing::info!(%vfs_path, ?change_kind, "Processing rust-analyzer.toml changes");
470 if vfs_path.as_path() == user_config_abs_path {
471 tracing::info!(%vfs_path, ?change_kind, "Use config rust-analyzer.toml changes");
472 change.change_user_config(Some(db.file_text(file_id).text(db).clone()));
473 }
474
475 let source_root_id = db.file_source_root(file_id).source_root_id(db);
478 let source_root = db.source_root(source_root_id).source_root(db);
479
480 if !source_root.is_library {
481 let entry = if workspace_ratoml_paths.contains(&vfs_path) {
482 tracing::info!(%vfs_path, ?source_root_id, "workspace rust-analyzer.toml changes");
483 change.change_workspace_ratoml(
484 source_root_id,
485 vfs_path.clone(),
486 Some(db.file_text(file_id).text(db).clone()),
487 )
488 } else {
489 tracing::info!(%vfs_path, ?source_root_id, "crate rust-analyzer.toml changes");
490 change.change_ratoml(
491 source_root_id,
492 vfs_path.clone(),
493 Some(db.file_text(file_id).text(db).clone()),
494 )
495 };
496
497 if let Some((kind, old_path, old_text)) = entry {
498 if old_path < vfs_path {
500 tracing::error!(
501 "Two `rust-analyzer.toml` files were found inside the same crate. {vfs_path} has no effect."
502 );
503 match kind {
505 RatomlFileKind::Crate => {
506 change.change_ratoml(source_root_id, old_path, old_text);
507 }
508 RatomlFileKind::Workspace => {
509 change.change_workspace_ratoml(
510 source_root_id,
511 old_path,
512 old_text,
513 );
514 }
515 }
516 }
517 }
518 } else {
519 tracing::info!(%vfs_path, "Ignoring library rust-analyzer.toml");
520 }
521 }
522 change.change_source_root_parent_map(self.local_roots_parent_map.clone());
523 change
524 };
525
526 let (config, e, should_update) = self.config.apply_change(config_change);
527 self.config_errors = e.is_empty().not().then_some(e);
528
529 if should_update {
530 self.update_configuration(config);
531 } else {
532 self.config = Arc::new(config);
534 }
535 }
536
537 {
543 if !matches!(&workspace_structure_change, Some((.., true))) {
544 _ = self.deferred_task_queue.sender.send(
545 crate::main_loop::DeferredTask::CheckProcMacroSources(modified_rust_files),
546 );
547 }
548 if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
552 let _p = span!(Level::INFO, "GlobalState::process_changes/ws_structure_change")
553 .entered();
554 self.enqueue_workspace_fetch(path, force_crate_graph_reload);
555 }
556 }
557
558 true
559 }
560
561 pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
562 GlobalStateSnapshot {
563 config: Arc::clone(&self.config),
564 workspaces: Arc::clone(&self.workspaces),
565 analysis: self.analysis_host.analysis(),
566 vfs: Arc::clone(&self.vfs),
567 minicore: self.minicore.clone(),
568 check_fixes: Arc::clone(&self.diagnostics.check_fixes),
569 mem_docs: self.mem_docs.clone(),
570 semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
571 proc_macros_loaded: !self.config.expand_proc_macros()
572 || self.fetch_proc_macros_queue.last_op_result().copied().unwrap_or(false),
573 flycheck: self.flycheck.clone(),
574 }
575 }
576
577 pub(crate) fn send_request<R: lsp_types::request::Request>(
578 &mut self,
579 params: R::Params,
580 handler: ReqHandler,
581 ) {
582 let request = self.req_queue.outgoing.register(R::METHOD.to_owned(), params, handler);
583 self.send(request.into());
584 }
585
586 pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
587 let handler = self
588 .req_queue
589 .outgoing
590 .complete(response.id.clone())
591 .expect("received response for unknown request");
592 handler(self, response)
593 }
594
595 pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
596 &self,
597 params: N::Params,
598 ) {
599 let not = lsp_server::Notification::new(N::METHOD.to_owned(), params);
600 self.send(not.into());
601 }
602
603 pub(crate) fn register_request(
604 &mut self,
605 request: &lsp_server::Request,
606 request_received: Instant,
607 ) {
608 self.req_queue
609 .incoming
610 .register(request.id.clone(), (request.method.clone(), request_received));
611 }
612
613 pub(crate) fn respond(&mut self, response: lsp_server::Response) {
614 if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) {
615 if let Some(err) = &response.error
616 && err.message.starts_with("server panicked")
617 {
618 self.poke_rust_analyzer_developer(format!("{}, check the log", err.message));
619 }
620
621 let duration = start.elapsed();
622 tracing::debug!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration));
623 self.send(response.into());
624 }
625 }
626
627 pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
628 if let Some(response) = self.req_queue.incoming.cancel(request_id) {
629 self.send(response.into());
630 }
631 }
632
633 pub(crate) fn is_completed(&self, request: &lsp_server::Request) -> bool {
634 self.req_queue.incoming.is_completed(&request.id)
635 }
636
637 #[track_caller]
638 fn send(&self, message: lsp_server::Message) {
639 self.sender.send(message).unwrap();
640 }
641
642 pub(crate) fn publish_diagnostics(
643 &mut self,
644 uri: Url,
645 version: Option<i32>,
646 mut diagnostics: Vec<lsp_types::Diagnostic>,
647 ) {
648 self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, {
650 let sender = self.sender.clone();
651 move |_| {
652 let patch_empty = |message: &mut String| {
662 if message.is_empty() {
663 " ".clone_into(message);
664 }
665 };
666
667 for d in &mut diagnostics {
668 patch_empty(&mut d.message);
669 if let Some(dri) = &mut d.related_information {
670 for dri in dri {
671 patch_empty(&mut dri.message);
672 }
673 }
674 }
675
676 let not = lsp_server::Notification::new(
677 <lsp_types::notification::PublishDiagnostics as lsp_types::notification::Notification>::METHOD.to_owned(),
678 lsp_types::PublishDiagnosticsParams { uri, diagnostics, version },
679 );
680 _ = sender.send(not.into());
681 }
682 });
683 }
684
685 pub(crate) fn check_workspaces_msrv(&self) -> impl Iterator<Item = String> + '_ {
686 self.workspaces.iter().filter_map(|ws| {
687 if let Some(toolchain) = &ws.toolchain
688 && *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION
689 {
690 return Some(format!(
691 "Workspace `{}` is using an outdated toolchain version `{}` but \
692 rust-analyzer only supports `{}` and higher.\n\
693 Consider using the rust-analyzer rustup component for your toolchain or
694 upgrade your toolchain to a supported version.\n\n",
695 ws.manifest_or_root(),
696 toolchain,
697 crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION,
698 ));
699 }
700 None
701 })
702 }
703
704 fn enqueue_workspace_fetch(&mut self, path: AbsPathBuf, force_crate_graph_reload: bool) {
705 let already_requested = self.fetch_workspaces_queue.op_requested()
706 && !self.fetch_workspaces_queue.op_in_progress();
707 if self.fetch_ws_receiver.is_none() && already_requested {
708 return;
714 }
715
716 self.fetch_ws_receiver = Some((
717 crossbeam_channel::after(Duration::from_millis(100)),
718 FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload },
719 ));
720 }
721
722 pub(crate) fn debounce_workspace_fetch(&mut self) {
723 if let Some((fetch_receiver, _)) = &mut self.fetch_ws_receiver {
724 *fetch_receiver = crossbeam_channel::after(Duration::from_millis(100));
725 }
726 }
727}
728
729impl Drop for GlobalState {
730 fn drop(&mut self) {
731 self.analysis_host.request_cancellation();
732 }
733}
734
735impl GlobalStateSnapshot {
736 fn vfs_read(&self) -> MappedRwLockReadGuard<'_, vfs::Vfs> {
737 RwLockReadGuard::map(self.vfs.read(), |(it, _)| it)
738 }
739
740 pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<Option<FileId>> {
742 url_to_file_id(&self.vfs_read(), url)
743 }
744
745 pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
746 file_id_to_url(&self.vfs_read(), id)
747 }
748
749 pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result<Option<FileId>> {
751 vfs_path_to_file_id(&self.vfs_read(), vfs_path)
752 }
753
754 pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
755 let endings = self.vfs.read().1[&file_id];
756 let index = self.analysis.file_line_index(file_id)?;
757 let res = LineIndex { index, endings, encoding: self.config.caps().negotiated_encoding() };
758 Ok(res)
759 }
760
761 pub(crate) fn file_version(&self, file_id: FileId) -> Option<i32> {
762 Some(self.mem_docs.get(self.vfs_read().file_path(file_id))?.version)
763 }
764
765 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
766 let path = from_proto::vfs_path(url).ok()?;
767 Some(self.mem_docs.get(&path)?.version)
768 }
769
770 pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
771 let mut base = self.vfs_read().file_path(path.anchor).clone();
772 base.pop();
773 let path = base.join(&path.path).unwrap();
774 let path = path.as_path().unwrap();
775 url_from_abs_path(path)
776 }
777
778 pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath {
779 self.vfs_read().file_path(file_id).clone()
780 }
781
782 pub(crate) fn target_spec_for_crate(&self, crate_id: Crate) -> Option<TargetSpec> {
783 let file_id = self.analysis.crate_root(crate_id).ok()?;
784 self.target_spec_for_file(file_id, crate_id)
785 }
786
787 pub(crate) fn target_spec_for_file(
788 &self,
789 file_id: FileId,
790 crate_id: Crate,
791 ) -> Option<TargetSpec> {
792 let path = self.vfs_read().file_path(file_id).clone();
793 let path = path.as_path()?;
794
795 for workspace in self.workspaces.iter() {
796 match &workspace.kind {
797 ProjectWorkspaceKind::Cargo { cargo, .. }
798 | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
799 let Some(target_idx) = cargo.target_by_root(path) else {
800 continue;
801 };
802
803 let target_data = &cargo[target_idx];
804 let package_data = &cargo[target_data.package];
805
806 return Some(TargetSpec::Cargo(CargoTargetSpec {
807 workspace_root: cargo.workspace_root().to_path_buf(),
808 cargo_toml: package_data.manifest.clone(),
809 crate_id,
810 package: cargo.package_flag(package_data),
811 package_id: package_data.id.clone(),
812 target: target_data.name.clone(),
813 target_kind: target_data.kind,
814 required_features: target_data.required_features.clone(),
815 features: package_data.features.keys().cloned().collect(),
816 sysroot_root: workspace.sysroot.root().map(ToOwned::to_owned),
817 }));
818 }
819 ProjectWorkspaceKind::Json(project) => {
820 let Some(krate) = project.crate_by_root(path) else {
821 continue;
822 };
823 let Some(build) = krate.build else {
824 continue;
825 };
826
827 return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec {
828 label: build.label,
829 target_kind: build.target_kind,
830 shell_runnables: project.runnables().to_owned(),
831 }));
832 }
833 ProjectWorkspaceKind::DetachedFile { .. } => {}
834 };
835 }
836
837 None
838 }
839
840 pub(crate) fn all_workspace_dependencies_for_package(
841 &self,
842 package: &Arc<PackageId>,
843 ) -> Option<FxHashSet<Arc<PackageId>>> {
844 for workspace in self.workspaces.iter() {
845 match &workspace.kind {
846 ProjectWorkspaceKind::Cargo { cargo, .. }
847 | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
848 let package = cargo.packages().find(|p| cargo[*p].id == *package)?;
849
850 return cargo[package]
851 .all_member_deps
852 .as_ref()
853 .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect());
854 }
855 _ => {}
856 }
857 }
858 None
859 }
860
861 pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
862 self.vfs.read().0.exists(file_id)
863 }
864
865 #[inline]
866 pub(crate) fn minicore(&self) -> MiniCore<'_> {
867 match &self.minicore.minicore_text {
868 Some(minicore) => MiniCore::new(minicore),
869 None => MiniCore::default(),
870 }
871 }
872}
873
874pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
875 let path = vfs.file_path(id);
876 let path = path.as_path().unwrap();
877 url_from_abs_path(path)
878}
879
880pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result<Option<FileId>> {
882 let path = from_proto::vfs_path(url)?;
883 vfs_path_to_file_id(vfs, &path)
884}
885
886pub(crate) fn vfs_path_to_file_id(
888 vfs: &vfs::Vfs,
889 vfs_path: &VfsPath,
890) -> anyhow::Result<Option<FileId>> {
891 let (file_id, excluded) =
892 vfs.file_id(vfs_path).ok_or_else(|| anyhow::format_err!("file not found: {vfs_path}"))?;
893 match excluded {
894 vfs::FileExcluded::Yes => Ok(None),
895 vfs::FileExcluded::No => Ok(Some(file_id)),
896 }
897}