From 5e3891c2559de5a6540d69bc14ded281484479f9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 9 Dec 2020 18:41:35 +0300 Subject: . --- crates/base_db/src/lib.rs | 7 ++++--- crates/vfs/src/anchored_path.rs | 39 +++++++++++++++++++++++++++++++++++++++ crates/vfs/src/file_set.rs | 8 ++++---- crates/vfs/src/lib.rs | 6 +++++- 4 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 crates/vfs/src/anchored_path.rs (limited to 'crates') diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 5571af495..a2cc1e099 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 000000000..02720a32e --- /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 9093fbd97..49ca593ac 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 cdf6f1fd0..a3be579a7 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)] -- cgit v1.2.3 From 6e24321e4579d25686982002ed18f321db23cb9f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 9 Dec 2020 19:01:15 +0300 Subject: Introduce anchored_path They allow to represent paths like `#[path = "C:\path.rs"] mod foo;` in a lossless cross-platform & network-transparent way. --- crates/base_db/src/lib.rs | 10 +---- crates/hir_def/src/nameres/mod_resolution.rs | 5 ++- crates/hir_def/src/test_db.rs | 6 +-- crates/hir_expand/src/builtin_macro.rs | 5 ++- crates/hir_expand/src/test_db.rs | 6 +-- crates/hir_ty/src/test_db.rs | 8 ++-- crates/ide/src/diagnostics.rs | 10 +++-- crates/ide/src/diagnostics/fixes.rs | 8 ++-- crates/ide/src/references/rename.rs | 57 +++++++++++++++++----------- crates/ide_db/src/lib.rs | 8 ++-- crates/ide_db/src/source_change.rs | 6 +-- crates/rust-analyzer/src/global_state.rs | 7 ++-- crates/rust-analyzer/src/to_proto.rs | 8 ++-- 13 files changed, 79 insertions(+), 65 deletions(-) (limited to 'crates') diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index a2cc1e099..595f28ada 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -91,12 +91,7 @@ pub const DEFAULT_LRU_CAP: usize = 128; pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; - /// Note that we intentionally accept a `&str` and not a `&Path` here. This - /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, - /// so the input is guaranteed to be utf-8 string. One might be tempted to - /// introduce some kind of "utf-8 path with / separators", but that's a bad idea. Behold - /// `#[path = "C://no/way"]` - fn resolve_path(&self, anchor: FileId, path: &str) -> Option; + fn resolve_path(&self, path: AnchoredPath) -> Option; fn relevant_crates(&self, file_id: FileId) -> Arc>; } @@ -155,8 +150,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { fn file_text(&self, file_id: FileId) -> Arc { SourceDatabaseExt::file_text(self.0, file_id) } - fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - let path = AnchoredPath { anchor, path }; + fn resolve_path(&self, path: AnchoredPath) -> Option { // FIXME: this *somehow* should be platform agnostic... let source_root = self.0.file_source_root(path.anchor); let source_root = self.0.source_root(source_root); diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs index c0c789cae..b4ccd4488 100644 --- a/crates/hir_def/src/nameres/mod_resolution.rs +++ b/crates/hir_def/src/nameres/mod_resolution.rs @@ -1,5 +1,5 @@ //! This module resolves `mod foo;` declaration to file. -use base_db::FileId; +use base_db::{AnchoredPath, FileId}; use hir_expand::name::Name; use syntax::SmolStr; use test_utils::mark; @@ -77,7 +77,8 @@ impl ModDir { }; for candidate in candidate_files.iter() { - if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) { + let path = AnchoredPath { anchor: file_id, path: candidate.as_str() }; + if let Some(file_id) = db.resolve_path(path) { let is_mod_rs = candidate.ends_with("mod.rs"); let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index f8b150850..574c0201a 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs @@ -5,8 +5,8 @@ use std::{ sync::{Arc, Mutex}, }; -use base_db::SourceDatabase; use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; +use base_db::{AnchoredPath, SourceDatabase}; use hir_expand::db::AstDatabase; use hir_expand::diagnostics::Diagnostic; use hir_expand::diagnostics::DiagnosticSinkBuilder; @@ -63,8 +63,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, path) + fn resolve_path(&self, path: AnchoredPath) -> Option { + FileLoaderDelegate(self).resolve_path(path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 79b970850..f60666a54 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs @@ -4,7 +4,7 @@ use crate::{ MacroDefId, MacroDefKind, TextSize, }; -use base_db::FileId; +use base_db::{AnchoredPath, FileId}; use either::Either; use mbe::{parse_to_token_tree, ExpandResult}; use parser::FragmentKind; @@ -324,7 +324,8 @@ fn relative_file( allow_recursion: bool, ) -> Option { let call_site = call_id.as_file().original_file(db); - let res = db.resolve_path(call_site, path)?; + let path = AnchoredPath { anchor: call_site, path }; + let res = db.resolve_path(path)?; // Prevent include itself if res == call_site && !allow_recursion { None diff --git a/crates/hir_expand/src/test_db.rs b/crates/hir_expand/src/test_db.rs index fca501e1f..7168a9462 100644 --- a/crates/hir_expand/src/test_db.rs +++ b/crates/hir_expand/src/test_db.rs @@ -5,7 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; -use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; +use base_db::{salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate}; use rustc_hash::FxHashSet; #[salsa::database( @@ -40,8 +40,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, path) + fn resolve_path(&self, path: AnchoredPath) -> Option { + FileLoaderDelegate(self).resolve_path(path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs index 22254b765..646e16bbe 100644 --- a/crates/hir_ty/src/test_db.rs +++ b/crates/hir_ty/src/test_db.rs @@ -5,7 +5,9 @@ use std::{ sync::{Arc, Mutex}, }; -use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; +use base_db::{ + salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, +}; use hir_def::{db::DefDatabase, ModuleId}; use hir_expand::db::AstDatabase; use rustc_hash::{FxHashMap, FxHashSet}; @@ -67,8 +69,8 @@ impl FileLoader for TestDB { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, path) + fn resolve_path(&self, path: AnchoredPath) -> Option { + FileLoaderDelegate(self).resolve_path(path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index c8453edb3..d09f3a0a1 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -610,10 +610,12 @@ fn test_fn() { source_file_edits: [], file_system_edits: [ CreateFile { - anchor: FileId( - 0, - ), - dst: "foo.rs", + dst: AnchoredPathBuf { + anchor: FileId( + 0, + ), + path: "foo.rs", + }, }, ], is_snippet: false, diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 24d08651e..29c7a040b 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs @@ -8,7 +8,7 @@ use hir::{ }, HasSource, HirDisplay, Semantics, VariantDef, }; -use ide_db::base_db::FileId; +use ide_db::base_db::{AnchoredPathBuf, FileId}; use ide_db::{ source_change::{FileSystemEdit, SourceFileEdit}, RootDatabase, @@ -36,8 +36,10 @@ impl DiagnosticWithFix for UnresolvedModule { Some(Fix::new( "Create module", FileSystemEdit::CreateFile { - anchor: self.file.original_file(sema.db), - dst: self.candidate.clone(), + dst: AnchoredPathBuf { + anchor: self.file.original_file(sema.db), + path: self.candidate.clone(), + }, } .into(), unresolved_module.syntax().text_range(), diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 64fe8bd65..44081f210 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -6,7 +6,7 @@ use std::{ }; use hir::{Module, ModuleDef, ModuleSource, Semantics}; -use ide_db::base_db::{FileRange, SourceDatabaseExt}; +use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, RootDatabase, @@ -182,12 +182,13 @@ fn rename_mod( match src.value { ModuleSource::SourceFile(..) => { // mod is defined in path/to/dir/mod.rs - let dst = if module.is_mod_rs(sema.db) { + let path = if module.is_mod_rs(sema.db) { format!("../{}/mod.rs", new_name) } else { format!("{}.rs", new_name) }; - let move_file = FileSystemEdit::MoveFile { src: file_id, anchor: file_id, dst }; + let dst = AnchoredPathBuf { anchor: file_id, path }; + let move_file = FileSystemEdit::MoveFile { src: file_id, dst }; file_system_edits.push(move_file); } ModuleSource::Module(..) => {} @@ -771,10 +772,12 @@ mod foo<|>; src: FileId( 2, ), - anchor: FileId( - 2, - ), - dst: "foo2.rs", + dst: AnchoredPathBuf { + anchor: FileId( + 2, + ), + path: "foo2.rs", + }, }, ], is_snippet: false, @@ -837,10 +840,12 @@ use crate::foo<|>::FooContent; src: FileId( 1, ), - anchor: FileId( - 1, - ), - dst: "quux.rs", + dst: AnchoredPathBuf { + anchor: FileId( + 1, + ), + path: "quux.rs", + }, }, ], is_snippet: false, @@ -884,10 +889,12 @@ mod fo<|>o; src: FileId( 1, ), - anchor: FileId( - 1, - ), - dst: "../foo2/mod.rs", + dst: AnchoredPathBuf { + anchor: FileId( + 1, + ), + path: "../foo2/mod.rs", + }, }, ], is_snippet: false, @@ -932,10 +939,12 @@ mod outer { mod fo<|>o; } src: FileId( 1, ), - anchor: FileId( - 1, - ), - dst: "bar.rs", + dst: AnchoredPathBuf { + anchor: FileId( + 1, + ), + path: "bar.rs", + }, }, ], is_snippet: false, @@ -1016,10 +1025,12 @@ pub mod foo<|>; src: FileId( 2, ), - anchor: FileId( - 2, - ), - dst: "foo2.rs", + dst: AnchoredPathBuf { + anchor: FileId( + 2, + ), + path: "foo2.rs", + }, }, ], is_snippet: false, diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index fceaa089a..118c090d7 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -19,8 +19,8 @@ use std::{fmt, sync::Arc}; use base_db::{ salsa::{self, Durability}, - Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, - Upcast, + AnchoredPath, Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, + SourceDatabase, Upcast, }; use hir::db::{AstDatabase, DefDatabase, HirDatabase}; use rustc_hash::FxHashSet; @@ -72,8 +72,8 @@ impl FileLoader for RootDatabase { fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } - fn resolve_path(&self, anchor: FileId, path: &str) -> Option { - FileLoaderDelegate(self).resolve_path(anchor, path) + fn resolve_path(&self, path: AnchoredPath) -> Option { + FileLoaderDelegate(self).resolve_path(path) } fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index f1590ec66..e87d98dad 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs @@ -3,7 +3,7 @@ //! //! It can be viewed as a dual for `AnalysisChange`. -use base_db::FileId; +use base_db::{AnchoredPathBuf, FileId}; use text_edit::TextEdit; #[derive(Default, Debug, Clone)] @@ -44,8 +44,8 @@ impl From> for SourceChange { #[derive(Debug, Clone)] pub enum FileSystemEdit { - CreateFile { anchor: FileId, dst: String }, - MoveFile { src: FileId, anchor: FileId, dst: String }, + CreateFile { dst: AnchoredPathBuf }, + MoveFile { src: FileId, dst: AnchoredPathBuf }, } impl From for SourceChange { diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index a27495d0d..71dc56915 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -13,6 +13,7 @@ use lsp_types::{SemanticTokens, Url}; use parking_lot::{Mutex, RwLock}; use project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; use rustc_hash::FxHashMap; +use vfs::AnchoredPathBuf; use crate::{ config::Config, @@ -268,10 +269,10 @@ impl GlobalStateSnapshot { Some(self.mem_docs.get(&path)?.version) } - pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { - let mut base = self.vfs.read().0.file_path(file_id); + pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url { + let mut base = self.vfs.read().0.file_path(path.anchor); base.pop(); - let path = base.join(path).unwrap(); + let path = base.join(&path.path).unwrap(); let path = path.as_path().unwrap(); url_from_abs_path(&path) } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 01eabe852..715f8927a 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -628,17 +628,17 @@ pub(crate) fn resource_op( file_system_edit: FileSystemEdit, ) -> lsp_types::ResourceOp { match file_system_edit { - FileSystemEdit::CreateFile { anchor, dst } => { - let uri = snap.anchored_path(anchor, &dst); + FileSystemEdit::CreateFile { dst } => { + let uri = snap.anchored_path(&dst); lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None, annotation: None, }) } - FileSystemEdit::MoveFile { src, anchor, dst } => { + FileSystemEdit::MoveFile { src, dst } => { let old_uri = snap.file_id_to_url(src); - let new_uri = snap.anchored_path(anchor, &dst); + let new_uri = snap.anchored_path(&dst); lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, -- cgit v1.2.3