diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 5571af4954..a2cc1e0997 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -18,7 +18,7 @@ pub use crate::{ }, }; pub use salsa; -pub use vfs::{file_set::FileSet, FileId, VfsPath}; +pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath}; #[macro_export] macro_rules! impl_intern_key { @@ -156,10 +156,11 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { SourceDatabaseExt::file_text(self.0, file_id) } fn resolve_path(&self, anchor: FileId, path: &str) -> Option { + let path = AnchoredPath { anchor, path }; // FIXME: this *somehow* should be platform agnostic... - let source_root = self.0.file_source_root(anchor); + let source_root = self.0.file_source_root(path.anchor); let source_root = self.0.source_root(source_root); - source_root.file_set.resolve_path(anchor, path) + source_root.file_set.resolve_path(path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { diff --git a/crates/vfs/src/anchored_path.rs b/crates/vfs/src/anchored_path.rs new file mode 100644 index 0000000000..02720a32e3 --- /dev/null +++ b/crates/vfs/src/anchored_path.rs @@ -0,0 +1,39 @@ +//! Analysis-level representation of file-system paths. +//! +//! The primary goal of this is to losslessly represent paths like +//! +//! ``` +//! #[path = "./bar.rs"] +//! mod foo; +//! ``` +//! +//! The first approach one might reach for is to use `PathBuf`. The problem here +//! is that `PathBuf` depends on host target (windows or linux), but +//! rust-analyzer should be capable to process `#[path = r"C:\bar.rs"]` on Unix. +//! +//! The second try is to use a `String`. This also fails, however. Consider a +//! hypothetical scenario, where rust-analyzer operates in a +//! networked/distributed mode. There's one global instance of rust-analyzer, +//! which processes requests from different machines. Now, the semantics of +//! `#[path = "/abs/path.rs"]` actually depends on which file-system we are at! +//! That is, even absolute paths exist relative to a file system! +//! +//! A more realistic scenario here is virtual VFS paths we use for testing. More +//! generally, there can be separate "universes" of VFS paths. +//! +//! That's why we use anchored representation -- each path carries an info about +//! a file this path originates from. We can fetch fs/"universe" information +//! from the anchor than. +use crate::FileId; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct AnchoredPathBuf { + pub anchor: FileId, + pub path: String, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct AnchoredPath<'a> { + pub anchor: FileId, + pub path: &'a str, +} diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 9093fbd979..49ca593ac8 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs @@ -7,7 +7,7 @@ use std::fmt; use fst::{IntoStreamer, Streamer}; use rustc_hash::FxHashMap; -use crate::{FileId, Vfs, VfsPath}; +use crate::{AnchoredPath, FileId, Vfs, VfsPath}; #[derive(Default, Clone, Eq, PartialEq)] pub struct FileSet { @@ -19,10 +19,10 @@ impl FileSet { pub fn len(&self) -> usize { self.files.len() } - pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - let mut base = self.paths[&anchor].clone(); + pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { + let mut base = self.paths[&path.anchor].clone(); base.pop(); - let path = base.join(path)?; + let path = base.join(path.path)?; self.files.get(&path).copied() } diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index cdf6f1fd02..a3be579a71 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -36,6 +36,7 @@ //! have a single `FileSet` which unions the two sources. mod vfs_path; mod path_interner; +mod anchored_path; pub mod file_set; pub mod loader; @@ -43,7 +44,10 @@ use std::{fmt, mem}; use crate::path_interner::PathInterner; -pub use crate::vfs_path::VfsPath; +pub use crate::{ + anchored_path::{AnchoredPath, AnchoredPathBuf}, + vfs_path::VfsPath, +}; pub use paths::{AbsPath, AbsPathBuf}; #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]