mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
internal: Enforce utf8 paths
This commit is contained in:
parent
ba339596bf
commit
399dbc074b
46 changed files with 383 additions and 319 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -732,6 +732,7 @@ dependencies = [
|
||||||
"ide-db",
|
"ide-db",
|
||||||
"itertools",
|
"itertools",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"paths",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"stdx",
|
"stdx",
|
||||||
"syntax",
|
"syntax",
|
||||||
|
@ -931,6 +932,7 @@ dependencies = [
|
||||||
"hir-expand",
|
"hir-expand",
|
||||||
"ide-db",
|
"ide-db",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"paths",
|
||||||
"proc-macro-api",
|
"proc-macro-api",
|
||||||
"project-model",
|
"project-model",
|
||||||
"span",
|
"span",
|
||||||
|
@ -1225,6 +1227,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paths"
|
name = "paths"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
|
@ -1598,6 +1603,7 @@ dependencies = [
|
||||||
"oorandom",
|
"oorandom",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"parser",
|
"parser",
|
||||||
|
"paths",
|
||||||
"proc-macro-api",
|
"proc-macro-api",
|
||||||
"profile",
|
"profile",
|
||||||
"project-model",
|
"project-model",
|
||||||
|
@ -2024,6 +2030,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
name = "toolchain"
|
name = "toolchain"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"camino",
|
||||||
"home",
|
"home",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,7 @@ anyhow = "1.0.75"
|
||||||
arrayvec = "0.7.4"
|
arrayvec = "0.7.4"
|
||||||
bitflags = "2.4.1"
|
bitflags = "2.4.1"
|
||||||
cargo_metadata = "0.18.1"
|
cargo_metadata = "0.18.1"
|
||||||
|
camino = "1.1.6"
|
||||||
chalk-solve = { version = "0.96.0", default-features = false }
|
chalk-solve = { version = "0.96.0", default-features = false }
|
||||||
chalk-ir = "0.96.0"
|
chalk-ir = "0.96.0"
|
||||||
chalk-recursive = { version = "0.96.0", default-features = false }
|
chalk-recursive = { version = "0.96.0", default-features = false }
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||||
|
|
||||||
use std::{fmt, io, path::PathBuf, process::Command, time::Duration};
|
use std::{fmt, io, process::Command, time::Duration};
|
||||||
|
|
||||||
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ pub enum FlycheckConfig {
|
||||||
extra_args: Vec<String>,
|
extra_args: Vec<String>,
|
||||||
extra_env: FxHashMap<String, String>,
|
extra_env: FxHashMap<String, String>,
|
||||||
ansi_color_output: bool,
|
ansi_color_output: bool,
|
||||||
target_dir: Option<PathBuf>,
|
target_dir: Option<Utf8PathBuf>,
|
||||||
},
|
},
|
||||||
CustomCommand {
|
CustomCommand {
|
||||||
command: String,
|
command: String,
|
||||||
|
@ -363,7 +363,7 @@ impl FlycheckActor {
|
||||||
});
|
});
|
||||||
|
|
||||||
cmd.arg("--manifest-path");
|
cmd.arg("--manifest-path");
|
||||||
cmd.arg(self.root.join("Cargo.toml").as_os_str());
|
cmd.arg(self.root.join("Cargo.toml"));
|
||||||
|
|
||||||
for target in target_triples {
|
for target in target_triples {
|
||||||
cmd.args(["--target", target.as_str()]);
|
cmd.args(["--target", target.as_str()]);
|
||||||
|
|
|
@ -26,6 +26,7 @@ text-edit.workspace = true
|
||||||
cfg.workspace = true
|
cfg.workspace = true
|
||||||
hir.workspace = true
|
hir.workspace = true
|
||||||
ide-db.workspace = true
|
ide-db.workspace = true
|
||||||
|
paths.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
expect-test = "1.4.0"
|
expect-test = "1.4.0"
|
||||||
|
|
|
@ -8,6 +8,7 @@ use ide_db::{
|
||||||
source_change::SourceChange,
|
source_change::SourceChange,
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
|
use paths::Utf8Component;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
|
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
|
||||||
AstNode, TextRange,
|
AstNode, TextRange,
|
||||||
|
@ -84,10 +85,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
|
||||||
|
|
||||||
// try resolving the relative difference of the paths as inline modules
|
// try resolving the relative difference of the paths as inline modules
|
||||||
let mut current = root_module;
|
let mut current = root_module;
|
||||||
for ele in rel.as_ref().components() {
|
for ele in rel.as_utf8_path().components() {
|
||||||
let seg = match ele {
|
let seg = match ele {
|
||||||
std::path::Component::Normal(seg) => seg.to_str()?,
|
Utf8Component::Normal(seg) => seg,
|
||||||
std::path::Component::RootDir => continue,
|
Utf8Component::RootDir => continue,
|
||||||
// shouldn't occur
|
// shouldn't occur
|
||||||
_ => continue 'crates,
|
_ => continue 'crates,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,8 +5,6 @@ mod tests;
|
||||||
|
|
||||||
mod intra_doc_links;
|
mod intra_doc_links;
|
||||||
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
|
|
||||||
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
|
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
|
||||||
use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
|
use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
|
@ -134,8 +132,8 @@ pub(crate) fn remove_links(markdown: &str) -> String {
|
||||||
pub(crate) fn external_docs(
|
pub(crate) fn external_docs(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
FilePosition { file_id, offset }: FilePosition,
|
FilePosition { file_id, offset }: FilePosition,
|
||||||
target_dir: Option<&OsStr>,
|
target_dir: Option<&str>,
|
||||||
sysroot: Option<&OsStr>,
|
sysroot: Option<&str>,
|
||||||
) -> Option<DocumentationLinks> {
|
) -> Option<DocumentationLinks> {
|
||||||
let sema = &Semantics::new(db);
|
let sema = &Semantics::new(db);
|
||||||
let file = sema.parse(file_id).syntax().clone();
|
let file = sema.parse(file_id).syntax().clone();
|
||||||
|
@ -331,8 +329,8 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)
|
||||||
fn get_doc_links(
|
fn get_doc_links(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
def: Definition,
|
def: Definition,
|
||||||
target_dir: Option<&OsStr>,
|
target_dir: Option<&str>,
|
||||||
sysroot: Option<&OsStr>,
|
sysroot: Option<&str>,
|
||||||
) -> DocumentationLinks {
|
) -> DocumentationLinks {
|
||||||
let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
|
let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
|
||||||
base_url.and_then(|url| url.join(path).ok())
|
base_url.and_then(|url| url.join(path).ok())
|
||||||
|
@ -479,15 +477,13 @@ fn map_links<'e>(
|
||||||
fn get_doc_base_urls(
|
fn get_doc_base_urls(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
def: Definition,
|
def: Definition,
|
||||||
target_dir: Option<&OsStr>,
|
target_dir: Option<&str>,
|
||||||
sysroot: Option<&OsStr>,
|
sysroot: Option<&str>,
|
||||||
) -> (Option<Url>, Option<Url>) {
|
) -> (Option<Url>, Option<Url>) {
|
||||||
let local_doc = target_dir
|
let local_doc = target_dir
|
||||||
.and_then(|path| path.to_str())
|
|
||||||
.and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
|
.and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
|
||||||
.and_then(|it| it.join("doc/").ok());
|
.and_then(|it| it.join("doc/").ok());
|
||||||
let system_doc = sysroot
|
let system_doc = sysroot
|
||||||
.and_then(|it| it.to_str())
|
|
||||||
.map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
|
.map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
|
||||||
.and_then(|it| Url::parse(&it).ok());
|
.and_then(|it| Url::parse(&it).ok());
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{ffi::OsStr, iter};
|
use std::iter;
|
||||||
|
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
use hir::Semantics;
|
use hir::Semantics;
|
||||||
|
@ -18,10 +18,10 @@ use crate::{
|
||||||
|
|
||||||
fn check_external_docs(
|
fn check_external_docs(
|
||||||
ra_fixture: &str,
|
ra_fixture: &str,
|
||||||
target_dir: Option<&OsStr>,
|
target_dir: Option<&str>,
|
||||||
expect_web_url: Option<Expect>,
|
expect_web_url: Option<Expect>,
|
||||||
expect_local_url: Option<Expect>,
|
expect_local_url: Option<Expect>,
|
||||||
sysroot: Option<&OsStr>,
|
sysroot: Option<&str>,
|
||||||
) {
|
) {
|
||||||
let (analysis, position) = fixture::position(ra_fixture);
|
let (analysis, position) = fixture::position(ra_fixture);
|
||||||
let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
|
let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
|
||||||
|
@ -127,10 +127,10 @@ fn external_docs_doc_builtin_type() {
|
||||||
//- /main.rs crate:foo
|
//- /main.rs crate:foo
|
||||||
let x: u3$02 = 0;
|
let x: u3$02 = 0;
|
||||||
"#,
|
"#,
|
||||||
Some(OsStr::new("/home/user/project")),
|
Some("/home/user/project"),
|
||||||
Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
|
Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
|
||||||
Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]),
|
Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]),
|
||||||
Some(OsStr::new("/sysroot")),
|
Some("/sysroot"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,10 +143,10 @@ use foo$0::Foo;
|
||||||
//- /lib.rs crate:foo
|
//- /lib.rs crate:foo
|
||||||
pub struct Foo;
|
pub struct Foo;
|
||||||
"#,
|
"#,
|
||||||
Some(OsStr::new("/home/user/project")),
|
Some("/home/user/project"),
|
||||||
Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]),
|
Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]),
|
||||||
Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]),
|
Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]),
|
||||||
Some(OsStr::new("/sysroot")),
|
Some("/sysroot"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,10 +157,10 @@ fn external_docs_doc_url_std_crate() {
|
||||||
//- /main.rs crate:std
|
//- /main.rs crate:std
|
||||||
use self$0;
|
use self$0;
|
||||||
"#,
|
"#,
|
||||||
Some(OsStr::new("/home/user/project")),
|
Some("/home/user/project"),
|
||||||
Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]),
|
Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]),
|
||||||
Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]),
|
Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]),
|
||||||
Some(OsStr::new("/sysroot")),
|
Some("/sysroot"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,10 +171,10 @@ fn external_docs_doc_url_struct() {
|
||||||
//- /main.rs crate:foo
|
//- /main.rs crate:foo
|
||||||
pub struct Fo$0o;
|
pub struct Fo$0o;
|
||||||
"#,
|
"#,
|
||||||
Some(OsStr::new("/home/user/project")),
|
Some("/home/user/project"),
|
||||||
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
||||||
Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]),
|
Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]),
|
||||||
Some(OsStr::new("/sysroot")),
|
Some("/sysroot"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,10 +185,10 @@ fn external_docs_doc_url_windows_backslash_path() {
|
||||||
//- /main.rs crate:foo
|
//- /main.rs crate:foo
|
||||||
pub struct Fo$0o;
|
pub struct Fo$0o;
|
||||||
"#,
|
"#,
|
||||||
Some(OsStr::new(r"C:\Users\user\project")),
|
Some(r"C:\Users\user\project"),
|
||||||
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
||||||
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
|
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
|
||||||
Some(OsStr::new("/sysroot")),
|
Some("/sysroot"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,10 +199,10 @@ fn external_docs_doc_url_windows_slash_path() {
|
||||||
//- /main.rs crate:foo
|
//- /main.rs crate:foo
|
||||||
pub struct Fo$0o;
|
pub struct Fo$0o;
|
||||||
"#,
|
"#,
|
||||||
Some(OsStr::new(r"C:/Users/user/project")),
|
Some("C:/Users/user/project"),
|
||||||
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
|
||||||
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
|
Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
|
||||||
Some(OsStr::new("/sysroot")),
|
Some("/sysroot"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,6 @@ mod view_item_tree;
|
||||||
mod view_memory_layout;
|
mod view_memory_layout;
|
||||||
mod view_mir;
|
mod view_mir;
|
||||||
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
|
|
||||||
use cfg::CfgOptions;
|
use cfg::CfgOptions;
|
||||||
use fetch_crates::CrateInfo;
|
use fetch_crates::CrateInfo;
|
||||||
use hir::ChangeWithProcMacros;
|
use hir::ChangeWithProcMacros;
|
||||||
|
@ -511,8 +509,8 @@ impl Analysis {
|
||||||
pub fn external_docs(
|
pub fn external_docs(
|
||||||
&self,
|
&self,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
target_dir: Option<&OsStr>,
|
target_dir: Option<&str>,
|
||||||
sysroot: Option<&OsStr>,
|
sysroot: Option<&str>,
|
||||||
) -> Cancellable<doc_links::DocumentationLinks> {
|
) -> Cancellable<doc_links::DocumentationLinks> {
|
||||||
self.with_db(|db| {
|
self.with_db(|db| {
|
||||||
doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
|
doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
|
||||||
|
|
|
@ -20,6 +20,7 @@ tracing.workspace = true
|
||||||
|
|
||||||
hir-expand.workspace = true
|
hir-expand.workspace = true
|
||||||
ide-db.workspace = true
|
ide-db.workspace = true
|
||||||
|
paths.workspace = true
|
||||||
proc-macro-api.workspace = true
|
proc-macro-api.workspace = true
|
||||||
project-model.workspace = true
|
project-model.workspace = true
|
||||||
span.workspace = true
|
span.workspace = true
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub fn load_workspace_at(
|
||||||
load_config: &LoadCargoConfig,
|
load_config: &LoadCargoConfig,
|
||||||
progress: &dyn Fn(String),
|
progress: &dyn Fn(String),
|
||||||
) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroServer>)> {
|
) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroServer>)> {
|
||||||
let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
|
let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root));
|
||||||
let root = ProjectManifest::discover_single(&root)?;
|
let root = ProjectManifest::discover_single(&root)?;
|
||||||
let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
|
let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,14 @@ rust-version.workspace = true
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
camino.workspace = true
|
||||||
# Adding this dep sadly puts a lot of rust-analyzer crates after the
|
# Adding this dep sadly puts a lot of rust-analyzer crates after the
|
||||||
# serde-derive crate. Even though we don't activate the derive feature here,
|
# serde-derive crate. Even though we don't activate the derive feature here,
|
||||||
# someone else in the crate graph certainly does!
|
# someone else in the crate graph certainly does!
|
||||||
# serde.workspace = true
|
# serde.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
serde1 = ["camino/serde1"]
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
|
@ -1,4 +1,4 @@
|
||||||
//! Thin wrappers around `std::path`, distinguishing between absolute and
|
//! Thin wrappers around `std::path`/`camino::path`, distinguishing between absolute and
|
||||||
//! relative paths.
|
//! relative paths.
|
||||||
|
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||||
|
@ -7,16 +7,24 @@ use std::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
fmt, ops,
|
fmt, ops,
|
||||||
path::{Component, Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Wrapper around an absolute [`PathBuf`].
|
pub use camino::*;
|
||||||
|
|
||||||
|
/// Wrapper around an absolute [`Utf8PathBuf`].
|
||||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||||
pub struct AbsPathBuf(PathBuf);
|
pub struct AbsPathBuf(Utf8PathBuf);
|
||||||
|
|
||||||
|
impl From<AbsPathBuf> for Utf8PathBuf {
|
||||||
|
fn from(AbsPathBuf(path_buf): AbsPathBuf) -> Utf8PathBuf {
|
||||||
|
path_buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<AbsPathBuf> for PathBuf {
|
impl From<AbsPathBuf> for PathBuf {
|
||||||
fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
|
fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
|
||||||
path_buf
|
path_buf.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,9 +35,21 @@ impl ops::Deref for AbsPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<Utf8Path> for AbsPathBuf {
|
||||||
|
fn as_ref(&self) -> &Utf8Path {
|
||||||
|
self.0.as_path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<OsStr> for AbsPathBuf {
|
||||||
|
fn as_ref(&self) -> &OsStr {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for AbsPathBuf {
|
impl AsRef<Path> for AbsPathBuf {
|
||||||
fn as_ref(&self) -> &Path {
|
fn as_ref(&self) -> &Path {
|
||||||
self.0.as_path()
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +65,9 @@ impl Borrow<AbsPath> for AbsPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<PathBuf> for AbsPathBuf {
|
impl TryFrom<Utf8PathBuf> for AbsPathBuf {
|
||||||
type Error = PathBuf;
|
type Error = Utf8PathBuf;
|
||||||
fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
|
fn try_from(path_buf: Utf8PathBuf) -> Result<AbsPathBuf, Utf8PathBuf> {
|
||||||
if !path_buf.is_absolute() {
|
if !path_buf.is_absolute() {
|
||||||
return Err(path_buf);
|
return Err(path_buf);
|
||||||
}
|
}
|
||||||
|
@ -55,10 +75,20 @@ impl TryFrom<PathBuf> for AbsPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for AbsPathBuf {
|
impl TryFrom<PathBuf> for AbsPathBuf {
|
||||||
type Error = PathBuf;
|
type Error = PathBuf;
|
||||||
fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
|
fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
|
||||||
AbsPathBuf::try_from(PathBuf::from(path))
|
if !path_buf.is_absolute() {
|
||||||
|
return Err(path_buf);
|
||||||
|
}
|
||||||
|
Ok(AbsPathBuf(Utf8PathBuf::from_path_buf(path_buf)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for AbsPathBuf {
|
||||||
|
type Error = Utf8PathBuf;
|
||||||
|
fn try_from(path: &str) -> Result<AbsPathBuf, Utf8PathBuf> {
|
||||||
|
AbsPathBuf::try_from(Utf8PathBuf::from(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,19 +104,31 @@ impl AbsPathBuf {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `path` is not absolute.
|
/// Panics if `path` is not absolute.
|
||||||
pub fn assert(path: PathBuf) -> AbsPathBuf {
|
pub fn assert(path: Utf8PathBuf) -> AbsPathBuf {
|
||||||
AbsPathBuf::try_from(path)
|
AbsPathBuf::try_from(path)
|
||||||
.unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
|
.unwrap_or_else(|path| panic!("expected absolute path, got {}", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrap the given absolute path in `AbsPathBuf`
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `path` is not absolute.
|
||||||
|
pub fn assert_utf8(path: PathBuf) -> AbsPathBuf {
|
||||||
|
AbsPathBuf::assert(
|
||||||
|
Utf8PathBuf::from_path_buf(path)
|
||||||
|
.unwrap_or_else(|path| panic!("expected utf8 path, got {}", path.display())),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerces to an `AbsPath` slice.
|
/// Coerces to an `AbsPath` slice.
|
||||||
///
|
///
|
||||||
/// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`.
|
/// Equivalent of [`Utf8PathBuf::as_path`] for `AbsPathBuf`.
|
||||||
pub fn as_path(&self) -> &AbsPath {
|
pub fn as_path(&self) -> &AbsPath {
|
||||||
AbsPath::assert(self.0.as_path())
|
AbsPath::assert(self.0.as_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`.
|
/// Equivalent of [`Utf8PathBuf::pop`] for `AbsPathBuf`.
|
||||||
///
|
///
|
||||||
/// Note that this won't remove the root component, so `self` will still be
|
/// Note that this won't remove the root component, so `self` will still be
|
||||||
/// absolute.
|
/// absolute.
|
||||||
|
@ -97,18 +139,30 @@ impl AbsPathBuf {
|
||||||
|
|
||||||
impl fmt::Display for AbsPathBuf {
|
impl fmt::Display for AbsPathBuf {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0.display(), f)
|
fmt::Display::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around an absolute [`Path`].
|
/// Wrapper around an absolute [`Utf8Path`].
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct AbsPath(Path);
|
pub struct AbsPath(Utf8Path);
|
||||||
|
|
||||||
|
impl AsRef<Utf8Path> for AbsPath {
|
||||||
|
fn as_ref(&self) -> &Utf8Path {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for AbsPath {
|
impl AsRef<Path> for AbsPath {
|
||||||
fn as_ref(&self) -> &Path {
|
fn as_ref(&self) -> &Path {
|
||||||
&self.0
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<OsStr> for AbsPath {
|
||||||
|
fn as_ref(&self) -> &OsStr {
|
||||||
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,9 +174,9 @@ impl ToOwned for AbsPath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Path> for &'a AbsPath {
|
impl<'a> TryFrom<&'a Utf8Path> for &'a AbsPath {
|
||||||
type Error = &'a Path;
|
type Error = &'a Utf8Path;
|
||||||
fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
|
fn try_from(path: &'a Utf8Path) -> Result<&'a AbsPath, &'a Utf8Path> {
|
||||||
if !path.is_absolute() {
|
if !path.is_absolute() {
|
||||||
return Err(path);
|
return Err(path);
|
||||||
}
|
}
|
||||||
|
@ -136,24 +190,24 @@ impl AbsPath {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `path` is not absolute.
|
/// Panics if `path` is not absolute.
|
||||||
pub fn assert(path: &Path) -> &AbsPath {
|
pub fn assert(path: &Utf8Path) -> &AbsPath {
|
||||||
assert!(path.is_absolute());
|
assert!(path.is_absolute());
|
||||||
unsafe { &*(path as *const Path as *const AbsPath) }
|
unsafe { &*(path as *const Utf8Path as *const AbsPath) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`Path::parent`] for `AbsPath`.
|
/// Equivalent of [`Utf8Path::parent`] for `AbsPath`.
|
||||||
pub fn parent(&self) -> Option<&AbsPath> {
|
pub fn parent(&self) -> Option<&AbsPath> {
|
||||||
self.0.parent().map(AbsPath::assert)
|
self.0.parent().map(AbsPath::assert)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards.
|
/// Equivalent of [`Utf8Path::join`] for `AbsPath` with an additional normalize step afterwards.
|
||||||
pub fn absolutize(&self, path: impl AsRef<Path>) -> AbsPathBuf {
|
pub fn absolutize(&self, path: impl AsRef<Utf8Path>) -> AbsPathBuf {
|
||||||
self.join(path).normalize()
|
self.join(path).normalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`Path::join`] for `AbsPath`.
|
/// Equivalent of [`Utf8Path::join`] for `AbsPath`.
|
||||||
pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
|
pub fn join(&self, path: impl AsRef<Utf8Path>) -> AbsPathBuf {
|
||||||
self.as_ref().join(path).try_into().unwrap()
|
Utf8Path::join(self.as_ref(), path).try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize the given path:
|
/// Normalize the given path:
|
||||||
|
@ -172,7 +226,7 @@ impl AbsPath {
|
||||||
AbsPathBuf(normalize_path(&self.0))
|
AbsPathBuf(normalize_path(&self.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`Path::to_path_buf`] for `AbsPath`.
|
/// Equivalent of [`Utf8Path::to_path_buf`] for `AbsPath`.
|
||||||
pub fn to_path_buf(&self) -> AbsPathBuf {
|
pub fn to_path_buf(&self) -> AbsPathBuf {
|
||||||
AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
|
AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -181,7 +235,7 @@ impl AbsPath {
|
||||||
panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430")
|
panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`Path::strip_prefix`] for `AbsPath`.
|
/// Equivalent of [`Utf8Path::strip_prefix`] for `AbsPath`.
|
||||||
///
|
///
|
||||||
/// Returns a relative path.
|
/// Returns a relative path.
|
||||||
pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
|
pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
|
||||||
|
@ -195,57 +249,61 @@ impl AbsPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
|
pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
|
||||||
Some((
|
Some((self.file_stem()?, self.extension()))
|
||||||
self.file_stem()?.to_str()?,
|
|
||||||
self.extension().and_then(|extension| extension.to_str()),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// region:delegate-methods
|
// region:delegate-methods
|
||||||
|
|
||||||
// Note that we deliberately don't implement `Deref<Target = Path>` here.
|
// Note that we deliberately don't implement `Deref<Target = Utf8Path>` here.
|
||||||
//
|
//
|
||||||
// The problem with `Path` is that it directly exposes convenience IO-ing
|
// The problem with `Utf8Path` is that it directly exposes convenience IO-ing
|
||||||
// methods. For example, `Path::exists` delegates to `fs::metadata`.
|
// methods. For example, `Utf8Path::exists` delegates to `fs::metadata`.
|
||||||
//
|
//
|
||||||
// For `AbsPath`, we want to make sure that this is a POD type, and that all
|
// For `AbsPath`, we want to make sure that this is a POD type, and that all
|
||||||
// IO goes via `fs`. That way, it becomes easier to mock IO when we need it.
|
// IO goes via `fs`. That way, it becomes easier to mock IO when we need it.
|
||||||
|
|
||||||
pub fn file_name(&self) -> Option<&OsStr> {
|
pub fn file_name(&self) -> Option<&str> {
|
||||||
self.0.file_name()
|
self.0.file_name()
|
||||||
}
|
}
|
||||||
pub fn extension(&self) -> Option<&OsStr> {
|
pub fn extension(&self) -> Option<&str> {
|
||||||
self.0.extension()
|
self.0.extension()
|
||||||
}
|
}
|
||||||
pub fn file_stem(&self) -> Option<&OsStr> {
|
pub fn file_stem(&self) -> Option<&str> {
|
||||||
self.0.file_stem()
|
self.0.file_stem()
|
||||||
}
|
}
|
||||||
pub fn as_os_str(&self) -> &OsStr {
|
pub fn as_os_str(&self) -> &OsStr {
|
||||||
self.0.as_os_str()
|
self.0.as_os_str()
|
||||||
}
|
}
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
#[deprecated(note = "use Display instead")]
|
#[deprecated(note = "use Display instead")]
|
||||||
pub fn display(&self) -> std::path::Display<'_> {
|
pub fn display(&self) -> ! {
|
||||||
self.0.display()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
#[deprecated(note = "use std::fs::metadata().is_ok() instead")]
|
#[deprecated(note = "use std::fs::metadata().is_ok() instead")]
|
||||||
pub fn exists(&self) -> bool {
|
pub fn exists(&self) -> ! {
|
||||||
self.0.exists()
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn components(&self) -> Utf8Components<'_> {
|
||||||
|
self.0.components()
|
||||||
}
|
}
|
||||||
// endregion:delegate-methods
|
// endregion:delegate-methods
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AbsPath {
|
impl fmt::Display for AbsPath {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0.display(), f)
|
fmt::Display::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around a relative [`PathBuf`].
|
/// Wrapper around a relative [`Utf8PathBuf`].
|
||||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||||
pub struct RelPathBuf(PathBuf);
|
pub struct RelPathBuf(Utf8PathBuf);
|
||||||
|
|
||||||
impl From<RelPathBuf> for PathBuf {
|
impl From<RelPathBuf> for Utf8PathBuf {
|
||||||
fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf {
|
fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf {
|
||||||
path_buf
|
path_buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,15 +315,21 @@ impl ops::Deref for RelPathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for RelPathBuf {
|
impl AsRef<Utf8Path> for RelPathBuf {
|
||||||
fn as_ref(&self) -> &Path {
|
fn as_ref(&self) -> &Utf8Path {
|
||||||
self.0.as_path()
|
self.0.as_path()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<PathBuf> for RelPathBuf {
|
impl AsRef<Path> for RelPathBuf {
|
||||||
type Error = PathBuf;
|
fn as_ref(&self) -> &Path {
|
||||||
fn try_from(path_buf: PathBuf) -> Result<RelPathBuf, PathBuf> {
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Utf8PathBuf> for RelPathBuf {
|
||||||
|
type Error = Utf8PathBuf;
|
||||||
|
fn try_from(path_buf: Utf8PathBuf) -> Result<RelPathBuf, Utf8PathBuf> {
|
||||||
if !path_buf.is_relative() {
|
if !path_buf.is_relative() {
|
||||||
return Err(path_buf);
|
return Err(path_buf);
|
||||||
}
|
}
|
||||||
|
@ -274,65 +338,79 @@ impl TryFrom<PathBuf> for RelPathBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for RelPathBuf {
|
impl TryFrom<&str> for RelPathBuf {
|
||||||
type Error = PathBuf;
|
type Error = Utf8PathBuf;
|
||||||
fn try_from(path: &str) -> Result<RelPathBuf, PathBuf> {
|
fn try_from(path: &str) -> Result<RelPathBuf, Utf8PathBuf> {
|
||||||
RelPathBuf::try_from(PathBuf::from(path))
|
RelPathBuf::try_from(Utf8PathBuf::from(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelPathBuf {
|
impl RelPathBuf {
|
||||||
/// Coerces to a `RelPath` slice.
|
/// Coerces to a `RelPath` slice.
|
||||||
///
|
///
|
||||||
/// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`.
|
/// Equivalent of [`Utf8PathBuf::as_path`] for `RelPathBuf`.
|
||||||
pub fn as_path(&self) -> &RelPath {
|
pub fn as_path(&self) -> &RelPath {
|
||||||
RelPath::new_unchecked(self.0.as_path())
|
RelPath::new_unchecked(self.0.as_path())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around a relative [`Path`].
|
/// Wrapper around a relative [`Utf8Path`].
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct RelPath(Path);
|
pub struct RelPath(Utf8Path);
|
||||||
|
|
||||||
|
impl AsRef<Utf8Path> for RelPath {
|
||||||
|
fn as_ref(&self) -> &Utf8Path {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for RelPath {
|
impl AsRef<Path> for RelPath {
|
||||||
fn as_ref(&self) -> &Path {
|
fn as_ref(&self) -> &Path {
|
||||||
&self.0
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelPath {
|
impl RelPath {
|
||||||
/// Creates a new `RelPath` from `path`, without checking if it is relative.
|
/// Creates a new `RelPath` from `path`, without checking if it is relative.
|
||||||
pub fn new_unchecked(path: &Path) -> &RelPath {
|
pub fn new_unchecked(path: &Utf8Path) -> &RelPath {
|
||||||
unsafe { &*(path as *const Path as *const RelPath) }
|
unsafe { &*(path as *const Utf8Path as *const RelPath) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent of [`Path::to_path_buf`] for `RelPath`.
|
/// Equivalent of [`Utf8Path::to_path_buf`] for `RelPath`.
|
||||||
pub fn to_path_buf(&self) -> RelPathBuf {
|
pub fn to_path_buf(&self) -> RelPathBuf {
|
||||||
RelPathBuf::try_from(self.0.to_path_buf()).unwrap()
|
RelPathBuf::try_from(self.0.to_path_buf()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_utf8_path(&self) -> &Utf8Path {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Taken from <https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85>
|
/// Taken from <https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85>
|
||||||
fn normalize_path(path: &Path) -> PathBuf {
|
fn normalize_path(path: &Utf8Path) -> Utf8PathBuf {
|
||||||
let mut components = path.components().peekable();
|
let mut components = path.components().peekable();
|
||||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() {
|
let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() {
|
||||||
components.next();
|
components.next();
|
||||||
PathBuf::from(c.as_os_str())
|
Utf8PathBuf::from(c.as_str())
|
||||||
} else {
|
} else {
|
||||||
PathBuf::new()
|
Utf8PathBuf::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
for component in components {
|
for component in components {
|
||||||
match component {
|
match component {
|
||||||
Component::Prefix(..) => unreachable!(),
|
Utf8Component::Prefix(..) => unreachable!(),
|
||||||
Component::RootDir => {
|
Utf8Component::RootDir => {
|
||||||
ret.push(component.as_os_str());
|
ret.push(component.as_str());
|
||||||
}
|
}
|
||||||
Component::CurDir => {}
|
Utf8Component::CurDir => {}
|
||||||
Component::ParentDir => {
|
Utf8Component::ParentDir => {
|
||||||
ret.pop();
|
ret.pop();
|
||||||
}
|
}
|
||||||
Component::Normal(c) => {
|
Utf8Component::Normal(c) => {
|
||||||
ret.push(c);
|
ret.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ snap = "1.1.0"
|
||||||
indexmap = "2.1.0"
|
indexmap = "2.1.0"
|
||||||
|
|
||||||
# local deps
|
# local deps
|
||||||
paths.workspace = true
|
paths = { workspace = true, features = ["serde1"] }
|
||||||
tt.workspace = true
|
tt.workspace = true
|
||||||
stdx.workspace = true
|
stdx.workspace = true
|
||||||
text-size.workspace = true
|
text-size.workspace = true
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
//! Defines messages for cross-process message passing based on `ndjson` wire protocol
|
//! Defines messages for cross-process message passing based on `ndjson` wire protocol
|
||||||
pub(crate) mod flat;
|
pub(crate) mod flat;
|
||||||
|
|
||||||
use std::{
|
use std::io::{self, BufRead, Write};
|
||||||
io::{self, BufRead, Write},
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use paths::Utf8PathBuf;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::ProcMacroKind;
|
use crate::ProcMacroKind;
|
||||||
|
@ -27,7 +25,7 @@ pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT;
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
/// Since [`NO_VERSION_CHECK_VERSION`]
|
/// Since [`NO_VERSION_CHECK_VERSION`]
|
||||||
ListMacros { dylib_path: PathBuf },
|
ListMacros { dylib_path: Utf8PathBuf },
|
||||||
/// Since [`NO_VERSION_CHECK_VERSION`]
|
/// Since [`NO_VERSION_CHECK_VERSION`]
|
||||||
ExpandMacro(Box<ExpandMacro>),
|
ExpandMacro(Box<ExpandMacro>),
|
||||||
/// Since [`VERSION_CHECK_VERSION`]
|
/// Since [`VERSION_CHECK_VERSION`]
|
||||||
|
@ -89,7 +87,7 @@ pub struct ExpandMacro {
|
||||||
/// Possible attributes for the attribute-like macros.
|
/// Possible attributes for the attribute-like macros.
|
||||||
pub attributes: Option<FlatTree>,
|
pub attributes: Option<FlatTree>,
|
||||||
|
|
||||||
pub lib: PathBuf,
|
pub lib: Utf8PathBuf,
|
||||||
|
|
||||||
/// Environment variables to set during macro expansion.
|
/// Environment variables to set during macro expansion.
|
||||||
pub env: Vec<(String, String)>,
|
pub env: Vec<(String, String)>,
|
||||||
|
@ -273,7 +271,7 @@ mod tests {
|
||||||
macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table),
|
macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table),
|
||||||
macro_name: Default::default(),
|
macro_name: Default::default(),
|
||||||
attributes: None,
|
attributes: None,
|
||||||
lib: std::env::current_dir().unwrap(),
|
lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
|
||||||
env: Default::default(),
|
env: Default::default(),
|
||||||
current_dir: Default::default(),
|
current_dir: Default::default(),
|
||||||
has_global_spans: ExpnGlobals {
|
has_global_spans: ExpnGlobals {
|
||||||
|
|
|
@ -175,7 +175,7 @@ fn mk_child(
|
||||||
env: &FxHashMap<String, String>,
|
env: &FxHashMap<String, String>,
|
||||||
null_stderr: bool,
|
null_stderr: bool,
|
||||||
) -> io::Result<Child> {
|
) -> io::Result<Child> {
|
||||||
let mut cmd = Command::new(path.as_os_str());
|
let mut cmd = Command::new(path);
|
||||||
cmd.envs(env)
|
cmd.envs(env)
|
||||||
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
|
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
|
@ -183,7 +183,7 @@ fn mk_child(
|
||||||
.stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() });
|
.stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() });
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
let mut path_var = std::ffi::OsString::new();
|
let mut path_var = std::ffi::OsString::new();
|
||||||
path_var.push(path.parent().unwrap().parent().unwrap().as_os_str());
|
path_var.push(path.parent().unwrap().parent().unwrap());
|
||||||
path_var.push("\\bin;");
|
path_var.push("\\bin;");
|
||||||
path_var.push(std::env::var_os("PATH").unwrap_or_default());
|
path_var.push(std::env::var_os("PATH").unwrap_or_default());
|
||||||
cmd.env("PATH", path_var);
|
cmd.env("PATH", path_var);
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
//! Handles dynamic library loading for proc macro
|
//! Handles dynamic library loading for proc macro
|
||||||
|
|
||||||
use std::{
|
use std::{fmt, fs::File, io};
|
||||||
fmt,
|
|
||||||
fs::File,
|
|
||||||
io,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use object::Object;
|
use object::Object;
|
||||||
use paths::AbsPath;
|
use paths::{AbsPath, Utf8Path, Utf8PathBuf};
|
||||||
use proc_macro::bridge;
|
use proc_macro::bridge;
|
||||||
use proc_macro_api::{read_dylib_info, ProcMacroKind};
|
use proc_macro_api::{read_dylib_info, ProcMacroKind};
|
||||||
|
|
||||||
|
@ -26,7 +21,7 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool {
|
||||||
symbol.contains(NEW_REGISTRAR_SYMBOL)
|
symbol.contains(NEW_REGISTRAR_SYMBOL)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
|
fn find_registrar_symbol(file: &Utf8Path) -> io::Result<Option<String>> {
|
||||||
let file = File::open(file)?;
|
let file = File::open(file)?;
|
||||||
let buffer = unsafe { Mmap::map(&file)? };
|
let buffer = unsafe { Mmap::map(&file)? };
|
||||||
|
|
||||||
|
@ -62,12 +57,12 @@ fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
|
||||||
///
|
///
|
||||||
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
|
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
|
fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
|
||||||
unsafe { Library::new(file) }
|
unsafe { Library::new(file) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
|
fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
|
||||||
use libloading::os::unix::Library as UnixLibrary;
|
use libloading::os::unix::Library as UnixLibrary;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
|
@ -116,14 +111,14 @@ struct ProcMacroLibraryLibloading {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcMacroLibraryLibloading {
|
impl ProcMacroLibraryLibloading {
|
||||||
fn open(file: &Path) -> Result<Self, LoadProcMacroDylibError> {
|
fn open(file: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> {
|
||||||
let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
|
let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
|
||||||
invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
|
invalid_data_err(format!("Cannot find registrar symbol in file {file}"))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let abs_file: &AbsPath = file.try_into().map_err(|_| {
|
let abs_file: &AbsPath = file
|
||||||
invalid_data_err(format!("expected an absolute path, got {}", file.display()))
|
.try_into()
|
||||||
})?;
|
.map_err(|_| invalid_data_err(format!("expected an absolute path, got {file}")))?;
|
||||||
let version_info = read_dylib_info(abs_file)?;
|
let version_info = read_dylib_info(abs_file)?;
|
||||||
|
|
||||||
let lib = load_library(file).map_err(invalid_data_err)?;
|
let lib = load_library(file).map_err(invalid_data_err)?;
|
||||||
|
@ -138,10 +133,10 @@ pub struct Expander {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expander {
|
impl Expander {
|
||||||
pub fn new(lib: &Path) -> Result<Expander, LoadProcMacroDylibError> {
|
pub fn new(lib: &Utf8Path) -> Result<Expander, LoadProcMacroDylibError> {
|
||||||
// Some libraries for dynamic loading require canonicalized path even when it is
|
// Some libraries for dynamic loading require canonicalized path even when it is
|
||||||
// already absolute
|
// already absolute
|
||||||
let lib = lib.canonicalize()?;
|
let lib = lib.canonicalize_utf8()?;
|
||||||
|
|
||||||
let lib = ensure_file_with_lock_free_access(&lib)?;
|
let lib = ensure_file_with_lock_free_access(&lib)?;
|
||||||
|
|
||||||
|
@ -176,30 +171,26 @@ impl Expander {
|
||||||
|
|
||||||
/// Copy the dylib to temp directory to prevent locking in Windows
|
/// Copy the dylib to temp directory to prevent locking in Windows
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
|
fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {
|
||||||
use std::collections::hash_map::RandomState;
|
use std::collections::hash_map::RandomState;
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::hash::{BuildHasher, Hasher};
|
use std::hash::{BuildHasher, Hasher};
|
||||||
|
|
||||||
if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() {
|
if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() {
|
||||||
return Ok(path.to_path_buf());
|
return Ok(path.to_path_buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut to = std::env::temp_dir();
|
let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap();
|
||||||
|
|
||||||
let file_name = path.file_name().ok_or_else(|| {
|
let file_name = path.file_name().ok_or_else(|| {
|
||||||
io::Error::new(
|
io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}"))
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
format!("File path is invalid: {}", path.display()),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Generate a unique number by abusing `HashMap`'s hasher.
|
// Generate a unique number by abusing `HashMap`'s hasher.
|
||||||
// Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
|
// Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
|
||||||
let t = RandomState::new().build_hasher().finish();
|
let t = RandomState::new().build_hasher().finish();
|
||||||
|
|
||||||
let mut unique_name = OsString::from(t.to_string());
|
let mut unique_name = t.to_string();
|
||||||
unique_name.push(file_name);
|
unique_name.push_str(file_name);
|
||||||
|
|
||||||
to.push(unique_name);
|
to.push(unique_name);
|
||||||
std::fs::copy(path, &to).unwrap();
|
std::fs::copy(path, &to).unwrap();
|
||||||
|
@ -207,6 +198,6 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
|
fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {
|
||||||
Ok(path.to_path_buf())
|
Ok(path.to_owned())
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,11 @@ use std::{
|
||||||
collections::{hash_map::Entry, HashMap},
|
collections::{hash_map::Entry, HashMap},
|
||||||
env,
|
env,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
fs,
|
fs, thread,
|
||||||
path::{Path, PathBuf},
|
|
||||||
thread,
|
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use paths::{Utf8Path, Utf8PathBuf};
|
||||||
use proc_macro_api::{
|
use proc_macro_api::{
|
||||||
msg::{
|
msg::{
|
||||||
self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals,
|
self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals,
|
||||||
|
@ -81,7 +80,7 @@ impl ProcMacroSrvSpan for Span {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ProcMacroSrv {
|
pub struct ProcMacroSrv {
|
||||||
expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
|
expanders: HashMap<(Utf8PathBuf, SystemTime), dylib::Expander>,
|
||||||
span_mode: SpanMode,
|
span_mode: SpanMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,23 +148,22 @@ impl ProcMacroSrv {
|
||||||
|
|
||||||
pub fn list_macros(
|
pub fn list_macros(
|
||||||
&mut self,
|
&mut self,
|
||||||
dylib_path: &Path,
|
dylib_path: &Utf8Path,
|
||||||
) -> Result<Vec<(String, ProcMacroKind)>, String> {
|
) -> Result<Vec<(String, ProcMacroKind)>, String> {
|
||||||
let expander = self.expander(dylib_path)?;
|
let expander = self.expander(dylib_path)?;
|
||||||
Ok(expander.list_macros())
|
Ok(expander.list_macros())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
|
fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> {
|
||||||
let time = fs::metadata(path)
|
let time = fs::metadata(path)
|
||||||
.and_then(|it| it.modified())
|
.and_then(|it| it.modified())
|
||||||
.map_err(|err| format!("Failed to get file metadata for {}: {err}", path.display()))?;
|
.map_err(|err| format!("Failed to get file metadata for {path}: {err}",))?;
|
||||||
|
|
||||||
Ok(match self.expanders.entry((path.to_path_buf(), time)) {
|
Ok(match self.expanders.entry((path.to_path_buf(), time)) {
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => v.insert(
|
||||||
v.insert(dylib::Expander::new(path).map_err(|err| {
|
dylib::Expander::new(path)
|
||||||
format!("Cannot create expander for {}: {err}", path.display())
|
.map_err(|err| format!("Cannot create expander for {path}: {err}",))?,
|
||||||
})?)
|
),
|
||||||
}
|
|
||||||
Entry::Occupied(e) => e.into_mut(),
|
Entry::Occupied(e) => e.into_mut(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -306,6 +304,6 @@ impl Drop for EnvSnapshot {
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
|
pub fn proc_macro_test_dylib_path() -> paths::Utf8PathBuf {
|
||||||
proc_macro_test::PROC_MACRO_TEST_LOCATION.into()
|
proc_macro_test::PROC_MACRO_TEST_LOCATION.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ itertools.workspace = true
|
||||||
# local deps
|
# local deps
|
||||||
base-db.workspace = true
|
base-db.workspace = true
|
||||||
cfg.workspace = true
|
cfg.workspace = true
|
||||||
paths.workspace = true
|
paths = { workspace = true, features = ["serde1"] }
|
||||||
stdx.workspace = true
|
stdx.workspace = true
|
||||||
toolchain.workspace = true
|
toolchain.workspace = true
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl WorkspaceBuildScripts {
|
||||||
cmd.args(&config.extra_args);
|
cmd.args(&config.extra_args);
|
||||||
|
|
||||||
cmd.arg("--manifest-path");
|
cmd.arg("--manifest-path");
|
||||||
cmd.arg(workspace_root.join("Cargo.toml").as_os_str());
|
cmd.arg(workspace_root.join("Cargo.toml"));
|
||||||
|
|
||||||
if let Some(target_dir) = &config.target_dir {
|
if let Some(target_dir) = &config.target_dir {
|
||||||
cmd.arg("--target-dir").arg(target_dir);
|
cmd.arg("--target-dir").arg(target_dir);
|
||||||
|
@ -354,16 +354,11 @@ impl WorkspaceBuildScripts {
|
||||||
}
|
}
|
||||||
// cargo_metadata crate returns default (empty) path for
|
// cargo_metadata crate returns default (empty) path for
|
||||||
// older cargos, which is not absolute, so work around that.
|
// older cargos, which is not absolute, so work around that.
|
||||||
let out_dir = mem::take(&mut message.out_dir).into_os_string();
|
let out_dir = mem::take(&mut message.out_dir);
|
||||||
if !out_dir.is_empty() {
|
if !out_dir.as_str().is_empty() {
|
||||||
let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir));
|
let out_dir = AbsPathBuf::assert(out_dir);
|
||||||
// inject_cargo_env(package, package_build_data);
|
// inject_cargo_env(package, package_build_data);
|
||||||
// NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
|
data.envs.push(("OUT_DIR".to_owned(), out_dir.as_str().to_owned()));
|
||||||
if let Some(out_dir) =
|
|
||||||
out_dir.as_os_str().to_str().map(|s| s.to_owned())
|
|
||||||
{
|
|
||||||
data.envs.push(("OUT_DIR".to_owned(), out_dir));
|
|
||||||
}
|
|
||||||
data.out_dir = Some(out_dir);
|
data.out_dir = Some(out_dir);
|
||||||
data.cfgs = cfgs;
|
data.cfgs = cfgs;
|
||||||
}
|
}
|
||||||
|
@ -377,8 +372,8 @@ impl WorkspaceBuildScripts {
|
||||||
if let Some(filename) =
|
if let Some(filename) =
|
||||||
message.filenames.iter().find(|name| is_dylib(name))
|
message.filenames.iter().find(|name| is_dylib(name))
|
||||||
{
|
{
|
||||||
let filename = AbsPathBuf::assert(PathBuf::from(&filename));
|
let filename = AbsPath::assert(filename);
|
||||||
data.proc_macro_dylib_path = Some(filename);
|
data.proc_macro_dylib_path = Some(filename.to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
//! See [`CargoWorkspace`].
|
//! See [`CargoWorkspace`].
|
||||||
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use base_db::Edition;
|
use base_db::Edition;
|
||||||
use cargo_metadata::{CargoOpt, MetadataCommand};
|
use cargo_metadata::{CargoOpt, MetadataCommand};
|
||||||
use la_arena::{Arena, Idx};
|
use la_arena::{Arena, Idx};
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::from_value;
|
use serde_json::from_value;
|
||||||
|
@ -100,7 +99,7 @@ pub struct CargoConfig {
|
||||||
pub invocation_strategy: InvocationStrategy,
|
pub invocation_strategy: InvocationStrategy,
|
||||||
pub invocation_location: InvocationLocation,
|
pub invocation_location: InvocationLocation,
|
||||||
/// Optional path to use instead of `target` when building
|
/// Optional path to use instead of `target` when building
|
||||||
pub target_dir: Option<PathBuf>,
|
pub target_dir: Option<Utf8PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Package = Idx<PackageData>;
|
pub type Package = Idx<PackageData>;
|
||||||
|
@ -262,7 +261,7 @@ impl CargoWorkspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
meta.current_dir(current_dir.as_os_str());
|
meta.current_dir(current_dir);
|
||||||
|
|
||||||
let mut other_options = vec![];
|
let mut other_options = vec![];
|
||||||
// cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
|
// cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
|
||||||
|
@ -351,7 +350,7 @@ impl CargoWorkspace {
|
||||||
id: id.repr.clone(),
|
id: id.repr.clone(),
|
||||||
name,
|
name,
|
||||||
version,
|
version,
|
||||||
manifest: AbsPathBuf::assert(manifest_path.into()).try_into().unwrap(),
|
manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(),
|
||||||
targets: Vec::new(),
|
targets: Vec::new(),
|
||||||
is_local,
|
is_local,
|
||||||
is_member,
|
is_member,
|
||||||
|
@ -370,7 +369,7 @@ impl CargoWorkspace {
|
||||||
let tgt = targets.alloc(TargetData {
|
let tgt = targets.alloc(TargetData {
|
||||||
package: pkg,
|
package: pkg,
|
||||||
name,
|
name,
|
||||||
root: AbsPathBuf::assert(src_path.into()),
|
root: AbsPathBuf::assert(src_path),
|
||||||
kind: TargetKind::new(&kind),
|
kind: TargetKind::new(&kind),
|
||||||
required_features,
|
required_features,
|
||||||
});
|
});
|
||||||
|
@ -393,11 +392,9 @@ impl CargoWorkspace {
|
||||||
packages[source].active_features.extend(node.features);
|
packages[source].active_features.extend(node.features);
|
||||||
}
|
}
|
||||||
|
|
||||||
let workspace_root =
|
let workspace_root = AbsPathBuf::assert(meta.workspace_root);
|
||||||
AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string()));
|
|
||||||
|
|
||||||
let target_directory =
|
let target_directory = AbsPathBuf::assert(meta.target_directory);
|
||||||
AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string()));
|
|
||||||
|
|
||||||
CargoWorkspace { packages, targets, workspace_root, target_directory }
|
CargoWorkspace { packages, targets, workspace_root, target_directory }
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,9 +126,8 @@ impl ProjectManifest {
|
||||||
entities
|
entities
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.map(|it| it.path().join("Cargo.toml"))
|
.map(|it| it.path().join("Cargo.toml"))
|
||||||
.filter(|it| it.exists())
|
.map(AbsPathBuf::try_from)
|
||||||
.map(AbsPathBuf::assert)
|
.filter_map(|it| it.ok()?.try_into().ok())
|
||||||
.filter_map(|it| it.try_into().ok())
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,10 +51,9 @@
|
||||||
|
|
||||||
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition};
|
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition};
|
||||||
use la_arena::RawIdx;
|
use la_arena::RawIdx;
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{de, Deserialize};
|
use serde::{de, Deserialize};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::cfg_flag::CfgFlag;
|
use crate::cfg_flag::CfgFlag;
|
||||||
|
|
||||||
|
@ -113,7 +112,7 @@ impl ProjectJson {
|
||||||
.unwrap_or_else(|| root_module.starts_with(base));
|
.unwrap_or_else(|| root_module.starts_with(base));
|
||||||
let (include, exclude) = match crate_data.source {
|
let (include, exclude) = match crate_data.source {
|
||||||
Some(src) => {
|
Some(src) => {
|
||||||
let absolutize = |dirs: Vec<PathBuf>| {
|
let absolutize = |dirs: Vec<Utf8PathBuf>| {
|
||||||
dirs.into_iter().map(absolutize_on_base).collect::<Vec<_>>()
|
dirs.into_iter().map(absolutize_on_base).collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
(absolutize(src.include_dirs), absolutize(src.exclude_dirs))
|
(absolutize(src.include_dirs), absolutize(src.exclude_dirs))
|
||||||
|
@ -176,15 +175,15 @@ impl ProjectJson {
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct ProjectJsonData {
|
pub struct ProjectJsonData {
|
||||||
sysroot: Option<PathBuf>,
|
sysroot: Option<Utf8PathBuf>,
|
||||||
sysroot_src: Option<PathBuf>,
|
sysroot_src: Option<Utf8PathBuf>,
|
||||||
crates: Vec<CrateData>,
|
crates: Vec<CrateData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
struct CrateData {
|
struct CrateData {
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
root_module: PathBuf,
|
root_module: Utf8PathBuf,
|
||||||
edition: EditionData,
|
edition: EditionData,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
version: Option<semver::Version>,
|
version: Option<semver::Version>,
|
||||||
|
@ -194,7 +193,7 @@ struct CrateData {
|
||||||
target: Option<String>,
|
target: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
env: FxHashMap<String, String>,
|
env: FxHashMap<String, String>,
|
||||||
proc_macro_dylib_path: Option<PathBuf>,
|
proc_macro_dylib_path: Option<Utf8PathBuf>,
|
||||||
is_workspace_member: Option<bool>,
|
is_workspace_member: Option<bool>,
|
||||||
source: Option<CrateSource>,
|
source: Option<CrateSource>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -238,8 +237,8 @@ struct DepData {
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
struct CrateSource {
|
struct CrateSource {
|
||||||
include_dirs: Vec<PathBuf>,
|
include_dirs: Vec<Utf8PathBuf>,
|
||||||
exclude_dirs: Vec<PathBuf>,
|
exclude_dirs: Vec<Utf8PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Error>
|
fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Error>
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
//! but we can't process `.rlib` and need source code instead. The source code
|
//! but we can't process `.rlib` and need source code instead. The source code
|
||||||
//! is typically installed with `rustup component add rust-src` command.
|
//! is typically installed with `rustup component add rust-src` command.
|
||||||
|
|
||||||
use std::{env, fs, iter, ops, path::PathBuf, process::Command, sync::Arc};
|
use std::{env, fs, iter, ops, process::Command, sync::Arc};
|
||||||
|
|
||||||
use anyhow::{format_err, Result};
|
use anyhow::{format_err, Result};
|
||||||
use base_db::CrateName;
|
use base_db::CrateName;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use la_arena::{Arena, Idx};
|
use la_arena::{Arena, Idx};
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use toolchain::{probe_for_binary, Tool};
|
use toolchain::{probe_for_binary, Tool};
|
||||||
|
|
||||||
|
@ -419,7 +419,7 @@ fn discover_sysroot_dir(
|
||||||
rustc.current_dir(current_dir).args(["--print", "sysroot"]);
|
rustc.current_dir(current_dir).args(["--print", "sysroot"]);
|
||||||
tracing::debug!("Discovering sysroot by {:?}", rustc);
|
tracing::debug!("Discovering sysroot by {:?}", rustc);
|
||||||
let stdout = utf8_stdout(rustc)?;
|
let stdout = utf8_stdout(rustc)?;
|
||||||
Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
|
Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option<AbsPathBuf> {
|
fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option<AbsPathBuf> {
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
use std::{
|
use std::ops::Deref;
|
||||||
ops::Deref,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use base_db::{CrateGraph, FileId, ProcMacroPaths};
|
use base_db::{CrateGraph, FileId, ProcMacroPaths};
|
||||||
use cfg::{CfgAtom, CfgDiff};
|
use cfg::{CfgAtom, CfgDiff};
|
||||||
use expect_test::{expect_file, ExpectFile};
|
use expect_test::{expect_file, ExpectFile};
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
@ -113,17 +110,16 @@ fn replace_root(s: &mut String, direction: bool) {
|
||||||
fn replace_fake_sys_root(s: &mut String) {
|
fn replace_fake_sys_root(s: &mut String) {
|
||||||
let fake_sysroot_path = get_test_path("fake-sysroot");
|
let fake_sysroot_path = get_test_path("fake-sysroot");
|
||||||
let fake_sysroot_path = if cfg!(windows) {
|
let fake_sysroot_path = if cfg!(windows) {
|
||||||
let normalized_path =
|
let normalized_path = fake_sysroot_path.as_str().replace('\\', r#"\\"#);
|
||||||
fake_sysroot_path.to_str().expect("expected str").replace('\\', r#"\\"#);
|
|
||||||
format!(r#"{}\\"#, normalized_path)
|
format!(r#"{}\\"#, normalized_path)
|
||||||
} else {
|
} else {
|
||||||
format!("{}/", fake_sysroot_path.to_str().expect("expected str"))
|
format!("{}/", fake_sysroot_path.as_str())
|
||||||
};
|
};
|
||||||
*s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$")
|
*s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_test_path(file: &str) -> PathBuf {
|
fn get_test_path(file: &str) -> Utf8PathBuf {
|
||||||
let base = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
let base = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
base.join("test_data").join(file)
|
base.join("test_data").join(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +135,7 @@ fn get_fake_sysroot() -> Sysroot {
|
||||||
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
|
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
|
||||||
let mut root = "$ROOT$".to_owned();
|
let mut root = "$ROOT$".to_owned();
|
||||||
replace_root(&mut root, true);
|
replace_root(&mut root, true);
|
||||||
let path = Path::new(&root);
|
let path = Utf8Path::new(&root);
|
||||||
let base = AbsPath::assert(path);
|
let base = AbsPath::assert(path);
|
||||||
ProjectJson::new(base, data)
|
ProjectJson::new(base, data)
|
||||||
}
|
}
|
||||||
|
@ -268,7 +264,7 @@ fn smoke_test_real_sysroot_cargo() {
|
||||||
|
|
||||||
let cargo_workspace = CargoWorkspace::new(meta);
|
let cargo_workspace = CargoWorkspace::new(meta);
|
||||||
let sysroot = Ok(Sysroot::discover(
|
let sysroot = Ok(Sysroot::discover(
|
||||||
AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))),
|
AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1173,7 +1173,6 @@ fn detached_files_to_crate_graph(
|
||||||
};
|
};
|
||||||
let display_name = detached_file
|
let display_name = detached_file
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.and_then(|os_str| os_str.to_str())
|
|
||||||
.map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
|
.map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
|
||||||
let detached_file_crate = crate_graph.add_crate_root(
|
let detached_file_crate = crate_graph.add_crate_root(
|
||||||
file_id,
|
file_id,
|
||||||
|
@ -1555,7 +1554,7 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) {
|
||||||
// CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
|
// CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
|
||||||
|
|
||||||
let manifest_dir = package.manifest.parent();
|
let manifest_dir = package.manifest.parent();
|
||||||
env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
|
env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned());
|
||||||
|
|
||||||
// Not always right, but works for common cases.
|
// Not always right, but works for common cases.
|
||||||
env.set("CARGO", "cargo".into());
|
env.set("CARGO", "cargo".into());
|
||||||
|
|
|
@ -43,6 +43,7 @@ nohash-hasher.workspace = true
|
||||||
always-assert = "0.2.0"
|
always-assert = "0.2.0"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
semver.workspace = true
|
semver.workspace = true
|
||||||
|
memchr = "2.7.1"
|
||||||
|
|
||||||
cfg.workspace = true
|
cfg.workspace = true
|
||||||
flycheck.workspace = true
|
flycheck.workspace = true
|
||||||
|
@ -63,7 +64,7 @@ parser.workspace = true
|
||||||
toolchain.workspace = true
|
toolchain.workspace = true
|
||||||
vfs-notify.workspace = true
|
vfs-notify.workspace = true
|
||||||
vfs.workspace = true
|
vfs.workspace = true
|
||||||
memchr = "2.7.1"
|
paths.workspace = true
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.3.9"
|
winapi = "0.3.9"
|
||||||
|
|
|
@ -190,7 +190,7 @@ fn run_server() -> anyhow::Result<()> {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
let cwd = env::current_dir()?;
|
let cwd = env::current_dir()?;
|
||||||
AbsPathBuf::assert(cwd)
|
AbsPathBuf::assert_utf8(cwd)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl flags::AnalysisStats {
|
||||||
|
|
||||||
let mut db_load_sw = self.stop_watch();
|
let mut db_load_sw = self.stop_watch();
|
||||||
|
|
||||||
let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path));
|
let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(&self.path));
|
||||||
let manifest = ProjectManifest::discover_single(&path)?;
|
let manifest = ProjectManifest::discover_single(&path)?;
|
||||||
|
|
||||||
let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
|
let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
|
||||||
|
|
|
@ -16,7 +16,7 @@ impl flags::Diagnostics {
|
||||||
let cargo_config =
|
let cargo_config =
|
||||||
CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() };
|
CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() };
|
||||||
let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv {
|
let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv {
|
||||||
let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(p));
|
let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p));
|
||||||
ProcMacroServerChoice::Explicit(path)
|
ProcMacroServerChoice::Explicit(path)
|
||||||
} else {
|
} else {
|
||||||
ProcMacroServerChoice::Sysroot
|
ProcMacroServerChoice::Sysroot
|
||||||
|
|
|
@ -283,7 +283,7 @@ impl flags::Lsif {
|
||||||
with_proc_macro_server: ProcMacroServerChoice::Sysroot,
|
with_proc_macro_server: ProcMacroServerChoice::Sysroot,
|
||||||
prefill_caches: false,
|
prefill_caches: false,
|
||||||
};
|
};
|
||||||
let path = AbsPathBuf::assert(env::current_dir()?.join(self.path));
|
let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path));
|
||||||
let manifest = ProjectManifest::discover_single(&path)?;
|
let manifest = ProjectManifest::discover_single(&path)?;
|
||||||
|
|
||||||
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
|
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
|
||||||
|
|
|
@ -27,7 +27,8 @@ impl flags::Scip {
|
||||||
with_proc_macro_server: ProcMacroServerChoice::Sysroot,
|
with_proc_macro_server: ProcMacroServerChoice::Sysroot,
|
||||||
prefill_caches: true,
|
prefill_caches: true,
|
||||||
};
|
};
|
||||||
let root = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&self.path)).normalize();
|
let root =
|
||||||
|
vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize();
|
||||||
|
|
||||||
let mut config = crate::config::Config::new(
|
let mut config = crate::config::Config::new(
|
||||||
root.clone(),
|
root.clone(),
|
||||||
|
@ -63,12 +64,7 @@ impl flags::Scip {
|
||||||
special_fields: Default::default(),
|
special_fields: Default::default(),
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
project_root: format!(
|
project_root: format!("file://{root}"),
|
||||||
"file://{}",
|
|
||||||
root.as_os_str()
|
|
||||||
.to_str()
|
|
||||||
.ok_or(anyhow::format_err!("Unable to normalize project_root path"))?
|
|
||||||
),
|
|
||||||
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
|
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
|
||||||
special_fields: Default::default(),
|
special_fields: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -216,7 +212,7 @@ fn get_relative_filepath(
|
||||||
rootpath: &vfs::AbsPathBuf,
|
rootpath: &vfs::AbsPathBuf,
|
||||||
file_id: ide::FileId,
|
file_id: ide::FileId,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_ref().to_str()?.to_owned())
|
Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
|
// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
|
||||||
|
|
|
@ -7,11 +7,7 @@
|
||||||
//! configure the server itself, feature flags are passed into analysis, and
|
//! configure the server itself, feature flags are passed into analysis, and
|
||||||
//! tweak things like automatic insertion of `()` in completions.
|
//! tweak things like automatic insertion of `()` in completions.
|
||||||
|
|
||||||
use std::{
|
use std::{fmt, iter, ops::Not};
|
||||||
fmt, iter,
|
|
||||||
ops::Not,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use cfg::{CfgAtom, CfgDiff};
|
use cfg::{CfgAtom, CfgDiff};
|
||||||
use flycheck::FlycheckConfig;
|
use flycheck::FlycheckConfig;
|
||||||
|
@ -27,6 +23,7 @@ use ide_db::{
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lsp_types::{ClientCapabilities, MarkupKind};
|
use lsp_types::{ClientCapabilities, MarkupKind};
|
||||||
|
use paths::{Utf8Path, Utf8PathBuf};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
|
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
|
||||||
};
|
};
|
||||||
|
@ -327,7 +324,7 @@ config_data! {
|
||||||
/// These directories will be ignored by rust-analyzer. They are
|
/// These directories will be ignored by rust-analyzer. They are
|
||||||
/// relative to the workspace root, and globs are not supported. You may
|
/// relative to the workspace root, and globs are not supported. You may
|
||||||
/// also need to add the folders to Code's `files.watcherExclude`.
|
/// also need to add the folders to Code's `files.watcherExclude`.
|
||||||
files_excludeDirs: Vec<PathBuf> = "[]",
|
files_excludeDirs: Vec<Utf8PathBuf> = "[]",
|
||||||
/// Controls file watching implementation.
|
/// Controls file watching implementation.
|
||||||
files_watcher: FilesWatcherDef = "\"client\"",
|
files_watcher: FilesWatcherDef = "\"client\"",
|
||||||
|
|
||||||
|
@ -516,7 +513,7 @@ config_data! {
|
||||||
/// This config takes a map of crate names with the exported proc-macro names to ignore as values.
|
/// This config takes a map of crate names with the exported proc-macro names to ignore as values.
|
||||||
procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",
|
procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",
|
||||||
/// Internal config, path to proc-macro server executable.
|
/// Internal config, path to proc-macro server executable.
|
||||||
procMacro_server: Option<PathBuf> = "null",
|
procMacro_server: Option<Utf8PathBuf> = "null",
|
||||||
|
|
||||||
/// Exclude imports from find-all-references.
|
/// Exclude imports from find-all-references.
|
||||||
references_excludeImports: bool = "false",
|
references_excludeImports: bool = "false",
|
||||||
|
@ -864,7 +861,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
self.detached_files =
|
self.detached_files =
|
||||||
get_field::<Vec<PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
|
get_field::<Vec<Utf8PathBuf>>(&mut json, &mut errors, "detachedFiles", None, "[]")
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(AbsPathBuf::assert)
|
.map(AbsPathBuf::assert)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -956,7 +953,7 @@ impl Config {
|
||||||
pub fn has_linked_projects(&self) -> bool {
|
pub fn has_linked_projects(&self) -> bool {
|
||||||
!self.data.linkedProjects.is_empty()
|
!self.data.linkedProjects.is_empty()
|
||||||
}
|
}
|
||||||
pub fn linked_manifests(&self) -> impl Iterator<Item = &Path> + '_ {
|
pub fn linked_manifests(&self) -> impl Iterator<Item = &Utf8Path> + '_ {
|
||||||
self.data.linkedProjects.iter().filter_map(|it| match it {
|
self.data.linkedProjects.iter().filter_map(|it| match it {
|
||||||
ManifestOrProjectJson::Manifest(p) => Some(&**p),
|
ManifestOrProjectJson::Manifest(p) => Some(&**p),
|
||||||
ManifestOrProjectJson::ProjectJson(_) => None,
|
ManifestOrProjectJson::ProjectJson(_) => None,
|
||||||
|
@ -1410,9 +1407,11 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_dir_from_config(&self) -> Option<PathBuf> {
|
fn target_dir_from_config(&self) -> Option<Utf8PathBuf> {
|
||||||
self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir {
|
self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir {
|
||||||
TargetDirectory::UseSubdirectory(true) => Some(PathBuf::from("target/rust-analyzer")),
|
TargetDirectory::UseSubdirectory(true) => {
|
||||||
|
Some(Utf8PathBuf::from("target/rust-analyzer"))
|
||||||
|
}
|
||||||
TargetDirectory::UseSubdirectory(false) => None,
|
TargetDirectory::UseSubdirectory(false) => None,
|
||||||
TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()),
|
TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()),
|
||||||
TargetDirectory::Directory(_) => None,
|
TargetDirectory::Directory(_) => None,
|
||||||
|
@ -1951,7 +1950,7 @@ where
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum ManifestOrProjectJson {
|
enum ManifestOrProjectJson {
|
||||||
Manifest(PathBuf),
|
Manifest(Utf8PathBuf),
|
||||||
ProjectJson(ProjectJsonData),
|
ProjectJson(ProjectJsonData),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2134,7 +2133,7 @@ pub enum MemoryLayoutHoverRenderKindDef {
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum TargetDirectory {
|
pub enum TargetDirectory {
|
||||||
UseSubdirectory(bool),
|
UseSubdirectory(bool),
|
||||||
Directory(PathBuf),
|
Directory(Utf8PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! _config_data {
|
macro_rules! _config_data {
|
||||||
|
@ -2263,7 +2262,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "string" },
|
"items": { "type": "string" },
|
||||||
},
|
},
|
||||||
"Vec<PathBuf>" => set! {
|
"Vec<Utf8PathBuf>" => set! {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "string" },
|
"items": { "type": "string" },
|
||||||
},
|
},
|
||||||
|
@ -2291,7 +2290,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"Option<String>" => set! {
|
"Option<String>" => set! {
|
||||||
"type": ["null", "string"],
|
"type": ["null", "string"],
|
||||||
},
|
},
|
||||||
"Option<PathBuf>" => set! {
|
"Option<Utf8PathBuf>" => set! {
|
||||||
"type": ["null", "string"],
|
"type": ["null", "string"],
|
||||||
},
|
},
|
||||||
"Option<bool>" => set! {
|
"Option<bool>" => set! {
|
||||||
|
@ -2774,7 +2773,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true)));
|
assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true)));
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer")))
|
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2793,10 +2792,10 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.data.cargo_targetDir,
|
config.data.cargo_targetDir,
|
||||||
Some(TargetDirectory::Directory(PathBuf::from("other_folder")))
|
Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("other_folder")))
|
matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder")))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -519,14 +519,13 @@ fn clippy_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescript
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::{config::Config, global_state::GlobalState};
|
use crate::{config::Config, global_state::GlobalState};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use expect_test::{expect_file, ExpectFile};
|
use expect_test::{expect_file, ExpectFile};
|
||||||
use lsp_types::ClientCapabilities;
|
use lsp_types::ClientCapabilities;
|
||||||
|
use paths::Utf8Path;
|
||||||
|
|
||||||
fn check(diagnostics_json: &str, expect: ExpectFile) {
|
fn check(diagnostics_json: &str, expect: ExpectFile) {
|
||||||
check_with_config(DiagnosticsMapConfig::default(), diagnostics_json, expect)
|
check_with_config(DiagnosticsMapConfig::default(), diagnostics_json, expect)
|
||||||
|
@ -534,7 +533,7 @@ mod tests {
|
||||||
|
|
||||||
fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) {
|
fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) {
|
||||||
let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
|
let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
|
||||||
let workspace_root: &AbsPath = Path::new("/test/").try_into().unwrap();
|
let workspace_root: &AbsPath = Utf8Path::new("/test/").try_into().unwrap();
|
||||||
let (sender, _) = crossbeam_channel::unbounded();
|
let (sender, _) = crossbeam_channel::unbounded();
|
||||||
let state = GlobalState::new(
|
let state = GlobalState::new(
|
||||||
sender,
|
sender,
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
io::Write as _,
|
io::Write as _,
|
||||||
path::PathBuf,
|
|
||||||
process::{self, Stdio},
|
process::{self, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,6 +26,7 @@ use lsp_types::{
|
||||||
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
||||||
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
|
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
|
||||||
};
|
};
|
||||||
|
use paths::Utf8PathBuf;
|
||||||
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
|
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use stdx::{format_to, never};
|
use stdx::{format_to, never};
|
||||||
|
@ -1737,8 +1737,8 @@ pub(crate) fn handle_open_docs(
|
||||||
_ => (None, None),
|
_ => (None, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sysroot = sysroot.map(|p| p.root().as_os_str());
|
let sysroot = sysroot.map(|p| p.root().as_str());
|
||||||
let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str());
|
let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_str());
|
||||||
|
|
||||||
let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else {
|
let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else {
|
||||||
return if snap.config.local_docs() {
|
return if snap.config.local_docs() {
|
||||||
|
@ -2043,7 +2043,7 @@ fn run_rustfmt(
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
RustfmtConfig::CustomCommand { command, args } => {
|
RustfmtConfig::CustomCommand { command, args } => {
|
||||||
let cmd = PathBuf::from(&command);
|
let cmd = Utf8PathBuf::from(&command);
|
||||||
let workspace = CargoTargetSpec::for_file(snap, file_id)?;
|
let workspace = CargoTargetSpec::for_file(snap, file_id)?;
|
||||||
let mut cmd = match workspace {
|
let mut cmd = match workspace {
|
||||||
Some(spec) => {
|
Some(spec) => {
|
||||||
|
|
|
@ -52,7 +52,7 @@ fn integrated_highlighting_benchmark() {
|
||||||
|
|
||||||
let file_id = {
|
let file_id = {
|
||||||
let file = workspace_to_load.join(file);
|
let file = workspace_to_load.join(file);
|
||||||
let path = VfsPath::from(AbsPathBuf::assert(file));
|
let path = VfsPath::from(AbsPathBuf::assert_utf8(file));
|
||||||
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
|
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ fn integrated_completion_benchmark() {
|
||||||
|
|
||||||
let file_id = {
|
let file_id = {
|
||||||
let file = workspace_to_load.join(file);
|
let file = workspace_to_load.join(file);
|
||||||
let path = VfsPath::from(AbsPathBuf::assert(file));
|
let path = VfsPath::from(AbsPathBuf::assert_utf8(file));
|
||||||
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
|
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ fn integrated_diagnostics_benchmark() {
|
||||||
|
|
||||||
let file_id = {
|
let file_id = {
|
||||||
let file = workspace_to_load.join(file);
|
let file = workspace_to_load.join(file);
|
||||||
let path = VfsPath::from(AbsPathBuf::assert(file));
|
let path = VfsPath::from(AbsPathBuf::assert_utf8(file));
|
||||||
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
|
vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
|
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
|
||||||
use std::{
|
use std::{
|
||||||
iter::once,
|
iter::once,
|
||||||
mem, path,
|
mem,
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ use ide::{
|
||||||
};
|
};
|
||||||
use ide_db::{rust_doc::format_docs, FxHasher};
|
use ide_db::{rust_doc::format_docs, FxHasher};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use paths::{Utf8Component, Utf8Prefix};
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
use vfs::AbsPath;
|
use vfs::AbsPath;
|
||||||
|
@ -816,9 +817,9 @@ pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url
|
||||||
/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
|
/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
|
||||||
pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url {
|
pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url {
|
||||||
let url = lsp_types::Url::from_file_path(path).unwrap();
|
let url = lsp_types::Url::from_file_path(path).unwrap();
|
||||||
match path.as_ref().components().next() {
|
match path.components().next() {
|
||||||
Some(path::Component::Prefix(prefix))
|
Some(Utf8Component::Prefix(prefix))
|
||||||
if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) =>
|
if matches!(prefix.kind(), Utf8Prefix::Disk(_) | Utf8Prefix::VerbatimDisk(_)) =>
|
||||||
{
|
{
|
||||||
// Need to lowercase driver letter
|
// Need to lowercase driver letter
|
||||||
}
|
}
|
||||||
|
@ -2730,12 +2731,12 @@ struct ProcMacro {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
fn test_lowercase_drive_letter() {
|
fn test_lowercase_drive_letter() {
|
||||||
use std::path::Path;
|
use paths::Utf8Path;
|
||||||
|
|
||||||
let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap());
|
let url = url_from_abs_path(Utf8Path::new("C:\\Test").try_into().unwrap());
|
||||||
assert_eq!(url.to_string(), "file:///c:/Test");
|
assert_eq!(url.to_string(), "file:///c:/Test");
|
||||||
|
|
||||||
let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap());
|
let url = url_from_abs_path(Utf8Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap());
|
||||||
assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
|
assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -714,10 +714,9 @@ impl GlobalState {
|
||||||
message += &format!(
|
message += &format!(
|
||||||
": {}",
|
": {}",
|
||||||
match dir.strip_prefix(self.config.root_path()) {
|
match dir.strip_prefix(self.config.root_path()) {
|
||||||
Some(relative_path) => relative_path.as_ref(),
|
Some(relative_path) => relative_path.as_utf8_path(),
|
||||||
None => dir.as_ref(),
|
None => dir.as_ref(),
|
||||||
}
|
}
|
||||||
.display()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,10 +185,8 @@ impl GlobalState {
|
||||||
message.push_str(
|
message.push_str(
|
||||||
"`rust-analyzer.linkedProjects` have been specified, which may be incorrect. Specified project paths:\n",
|
"`rust-analyzer.linkedProjects` have been specified, which may be incorrect. Specified project paths:\n",
|
||||||
);
|
);
|
||||||
message.push_str(&format!(
|
message
|
||||||
" {}",
|
.push_str(&format!(" {}", self.config.linked_manifests().format("\n ")));
|
||||||
self.config.linked_manifests().map(|it| it.display()).format("\n ")
|
|
||||||
));
|
|
||||||
if self.config.has_linked_project_jsons() {
|
if self.config.has_linked_project_jsons() {
|
||||||
message.push_str("\nAdditionally, one or more project jsons are specified")
|
message.push_str("\nAdditionally, one or more project jsons are specified")
|
||||||
}
|
}
|
||||||
|
@ -739,7 +737,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
|
||||||
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
|
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
|
||||||
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
|
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
|
||||||
|
|
||||||
let file_name = match path.file_name().unwrap_or_default().to_str() {
|
let file_name = match path.file_name() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
@ -754,18 +752,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
|
||||||
// .cargo/config{.toml}
|
// .cargo/config{.toml}
|
||||||
if path.extension().unwrap_or_default() != "rs" {
|
if path.extension().unwrap_or_default() != "rs" {
|
||||||
let is_cargo_config = matches!(file_name, "config.toml" | "config")
|
let is_cargo_config = matches!(file_name, "config.toml" | "config")
|
||||||
&& path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false);
|
&& path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false);
|
||||||
return is_cargo_config;
|
return is_cargo_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
|
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_str().ends_with(it)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let parent = match path.parent() {
|
let parent = match path.parent() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) {
|
if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_str().ends_with(it)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if file_name == "main.rs" {
|
if file_name == "main.rs" {
|
||||||
|
@ -773,7 +771,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) {
|
if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_str().ends_with(it)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ fn get_fake_sysroot() -> Sysroot {
|
||||||
let sysroot_path = get_fake_sysroot_path();
|
let sysroot_path = get_fake_sysroot_path();
|
||||||
// there's no `libexec/` directory with a `proc-macro-srv` binary in that
|
// there's no `libexec/` directory with a `proc-macro-srv` binary in that
|
||||||
// fake sysroot, so we give them both the same path:
|
// fake sysroot, so we give them both the same path:
|
||||||
let sysroot_dir = AbsPathBuf::assert(sysroot_path);
|
let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path);
|
||||||
let sysroot_src_dir = sysroot_dir.clone();
|
let sysroot_src_dir = sysroot_dir.clone();
|
||||||
Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false)
|
Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -917,7 +917,7 @@ fn resolve_proc_macro() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let sysroot = project_model::Sysroot::discover_no_source(
|
let sysroot = project_model::Sysroot::discover_no_source(
|
||||||
&AbsPathBuf::assert(std::env::current_dir().unwrap()),
|
&AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1002,7 +1002,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
|
||||||
},
|
},
|
||||||
"procMacro": {
|
"procMacro": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"server": proc_macro_server_path.as_path().as_ref(),
|
"server": proc_macro_server_path.as_path().as_str(),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.root("foo")
|
.root("foo")
|
||||||
|
@ -1039,7 +1039,7 @@ fn test_will_rename_files_same_level() {
|
||||||
|
|
||||||
let tmp_dir = TestDir::new();
|
let tmp_dir = TestDir::new();
|
||||||
let tmp_dir_path = tmp_dir.path().to_owned();
|
let tmp_dir_path = tmp_dir.path().to_owned();
|
||||||
let tmp_dir_str = tmp_dir_path.to_str().unwrap();
|
let tmp_dir_str = tmp_dir_path.as_str();
|
||||||
let base_path = PathBuf::from(format!("file://{tmp_dir_str}"));
|
let base_path = PathBuf::from(format!("file://{tmp_dir_str}"));
|
||||||
|
|
||||||
let code = r#"
|
let code = r#"
|
||||||
|
@ -1084,7 +1084,7 @@ use crate::old_folder::nested::foo as bar;
|
||||||
"documentChanges": [
|
"documentChanges": [
|
||||||
{
|
{
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")),
|
"uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")),
|
||||||
"version": null
|
"version": null
|
||||||
},
|
},
|
||||||
"edits": [
|
"edits": [
|
||||||
|
@ -1141,7 +1141,7 @@ use crate::old_folder::nested::foo as bar;
|
||||||
"documentChanges": [
|
"documentChanges": [
|
||||||
{
|
{
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")),
|
"uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")),
|
||||||
"version": null
|
"version": null
|
||||||
},
|
},
|
||||||
"edits": [
|
"edits": [
|
||||||
|
@ -1162,7 +1162,7 @@ use crate::old_folder::nested::foo as bar;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")),
|
"uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")),
|
||||||
"version": null
|
"version": null
|
||||||
},
|
},
|
||||||
"edits": [
|
"edits": [
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Once,
|
sync::Once,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
@ -9,6 +8,7 @@ use std::{
|
||||||
use crossbeam_channel::{after, select, Receiver};
|
use crossbeam_channel::{after, select, Receiver};
|
||||||
use lsp_server::{Connection, Message, Notification, Request};
|
use lsp_server::{Connection, Message, Notification, Request};
|
||||||
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
|
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
|
||||||
|
use paths::{Utf8Path, Utf8PathBuf};
|
||||||
use rust_analyzer::{config::Config, lsp, main_loop};
|
use rust_analyzer::{config::Config, lsp, main_loop};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::{json, to_string_pretty, Value};
|
use serde_json::{json, to_string_pretty, Value};
|
||||||
|
@ -21,7 +21,7 @@ use crate::testdir::TestDir;
|
||||||
pub(crate) struct Project<'a> {
|
pub(crate) struct Project<'a> {
|
||||||
fixture: &'a str,
|
fixture: &'a str,
|
||||||
tmp_dir: Option<TestDir>,
|
tmp_dir: Option<TestDir>,
|
||||||
roots: Vec<PathBuf>,
|
roots: Vec<Utf8PathBuf>,
|
||||||
config: serde_json::Value,
|
config: serde_json::Value,
|
||||||
root_dir_contains_symlink: bool,
|
root_dir_contains_symlink: bool,
|
||||||
}
|
}
|
||||||
|
@ -359,7 +359,7 @@ impl Server {
|
||||||
self.client.sender.send(Message::Notification(not)).unwrap();
|
self.client.sender.send(Message::Notification(not)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path(&self) -> &Path {
|
pub(crate) fn path(&self) -> &Utf8Path {
|
||||||
self.dir.path()
|
self.dir.path()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs, io,
|
fs, io,
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use paths::{Utf8Path, Utf8PathBuf};
|
||||||
|
|
||||||
pub(crate) struct TestDir {
|
pub(crate) struct TestDir {
|
||||||
path: PathBuf,
|
path: Utf8PathBuf,
|
||||||
keep: bool,
|
keep: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,10 +52,13 @@ impl TestDir {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap();
|
std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap();
|
||||||
|
|
||||||
return TestDir { path: symlink_path, keep: false };
|
return TestDir {
|
||||||
|
path: Utf8PathBuf::from_path_buf(symlink_path).unwrap(),
|
||||||
|
keep: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return TestDir { path, keep: false };
|
return TestDir { path: Utf8PathBuf::from_path_buf(path).unwrap(), keep: false };
|
||||||
}
|
}
|
||||||
panic!("Failed to create a temporary directory")
|
panic!("Failed to create a temporary directory")
|
||||||
}
|
}
|
||||||
|
@ -64,7 +68,7 @@ impl TestDir {
|
||||||
self.keep = true;
|
self.keep = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub(crate) fn path(&self) -> &Path {
|
pub(crate) fn path(&self) -> &Utf8Path {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +83,7 @@ impl Drop for TestDir {
|
||||||
let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap());
|
let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap());
|
||||||
|
|
||||||
if let Some(actual_path) = actual_path {
|
if let Some(actual_path) = actual_path {
|
||||||
remove_dir_all(&actual_path).unwrap_or_else(|err| {
|
remove_dir_all(Utf8Path::from_path(&actual_path).unwrap()).unwrap_or_else(|err| {
|
||||||
panic!(
|
panic!(
|
||||||
"failed to remove temporary link to directory {}: {err}",
|
"failed to remove temporary link to directory {}: {err}",
|
||||||
actual_path.display()
|
actual_path.display()
|
||||||
|
@ -88,18 +92,18 @@ impl Drop for TestDir {
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_dir_all(&self.path).unwrap_or_else(|err| {
|
remove_dir_all(&self.path).unwrap_or_else(|err| {
|
||||||
panic!("failed to remove temporary directory {}: {err}", self.path.display())
|
panic!("failed to remove temporary directory {}: {err}", self.path)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn remove_dir_all(path: &Path) -> io::Result<()> {
|
fn remove_dir_all(path: &Utf8Path) -> io::Result<()> {
|
||||||
fs::remove_dir_all(path)
|
fs::remove_dir_all(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn remove_dir_all(path: &Path) -> io::Result<()> {
|
fn remove_dir_all(path: &Utf8Path) -> io::Result<()> {
|
||||||
for _ in 0..99 {
|
for _ in 0..99 {
|
||||||
if fs::remove_dir_all(path).is_ok() {
|
if fs::remove_dir_all(path).is_ok() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
@ -13,6 +13,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
home = "0.5.4"
|
home = "0.5.4"
|
||||||
|
camino.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||||
|
|
||||||
use std::{
|
use std::{env, iter, path::PathBuf};
|
||||||
env, iter,
|
|
||||||
path::{Path, PathBuf},
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum Tool {
|
pub enum Tool {
|
||||||
|
@ -16,7 +15,7 @@ pub enum Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tool {
|
impl Tool {
|
||||||
pub fn proxy(self) -> Option<PathBuf> {
|
pub fn proxy(self) -> Option<Utf8PathBuf> {
|
||||||
cargo_proxy(self.name())
|
cargo_proxy(self.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ impl Tool {
|
||||||
/// example: for cargo, this tries all paths in $PATH with appended `cargo`, returning the
|
/// example: for cargo, this tries all paths in $PATH with appended `cargo`, returning the
|
||||||
/// first that exists
|
/// first that exists
|
||||||
/// 4) If all else fails, we just try to use the executable name directly
|
/// 4) If all else fails, we just try to use the executable name directly
|
||||||
pub fn prefer_proxy(self) -> PathBuf {
|
pub fn prefer_proxy(self) -> Utf8PathBuf {
|
||||||
invoke(&[cargo_proxy, lookup_as_env_var, lookup_in_path], self.name())
|
invoke(&[cargo_proxy, lookup_as_env_var, lookup_in_path], self.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,11 +49,11 @@ impl Tool {
|
||||||
/// example: for cargo, this tries $CARGO_HOME/bin/cargo, or ~/.cargo/bin/cargo if $CARGO_HOME is unset.
|
/// example: for cargo, this tries $CARGO_HOME/bin/cargo, or ~/.cargo/bin/cargo if $CARGO_HOME is unset.
|
||||||
/// It seems that this is a reasonable place to try for cargo, rustc, and rustup
|
/// It seems that this is a reasonable place to try for cargo, rustc, and rustup
|
||||||
/// 4) If all else fails, we just try to use the executable name directly
|
/// 4) If all else fails, we just try to use the executable name directly
|
||||||
pub fn path(self) -> PathBuf {
|
pub fn path(self) -> Utf8PathBuf {
|
||||||
invoke(&[lookup_as_env_var, lookup_in_path, cargo_proxy], self.name())
|
invoke(&[lookup_as_env_var, lookup_in_path, cargo_proxy], self.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_in(self, path: &Path) -> Option<PathBuf> {
|
pub fn path_in(self, path: &Utf8Path) -> Option<Utf8PathBuf> {
|
||||||
probe_for_binary(path.join(self.name()))
|
probe_for_binary(path.join(self.name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,42 +67,50 @@ impl Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invoke(list: &[fn(&str) -> Option<PathBuf>], executable: &str) -> PathBuf {
|
fn invoke(list: &[fn(&str) -> Option<Utf8PathBuf>], executable: &str) -> Utf8PathBuf {
|
||||||
list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into())
|
list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks up the binary as its SCREAMING upper case in the env variables.
|
/// Looks up the binary as its SCREAMING upper case in the env variables.
|
||||||
fn lookup_as_env_var(executable_name: &str) -> Option<PathBuf> {
|
fn lookup_as_env_var(executable_name: &str) -> Option<Utf8PathBuf> {
|
||||||
env::var_os(executable_name.to_ascii_uppercase()).map(Into::into)
|
env::var_os(executable_name.to_ascii_uppercase())
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.map(Utf8PathBuf::try_from)
|
||||||
|
.and_then(Result::ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks up the binary in the cargo home directory if it exists.
|
/// Looks up the binary in the cargo home directory if it exists.
|
||||||
fn cargo_proxy(executable_name: &str) -> Option<PathBuf> {
|
fn cargo_proxy(executable_name: &str) -> Option<Utf8PathBuf> {
|
||||||
let mut path = get_cargo_home()?;
|
let mut path = get_cargo_home()?;
|
||||||
path.push("bin");
|
path.push("bin");
|
||||||
path.push(executable_name);
|
path.push(executable_name);
|
||||||
probe_for_binary(path)
|
probe_for_binary(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cargo_home() -> Option<PathBuf> {
|
fn get_cargo_home() -> Option<Utf8PathBuf> {
|
||||||
if let Some(path) = env::var_os("CARGO_HOME") {
|
if let Some(path) = env::var_os("CARGO_HOME") {
|
||||||
return Some(path.into());
|
return Utf8PathBuf::try_from(PathBuf::from(path)).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut path) = home::home_dir() {
|
if let Some(mut path) = home::home_dir() {
|
||||||
path.push(".cargo");
|
path.push(".cargo");
|
||||||
return Some(path);
|
return Utf8PathBuf::try_from(path).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_in_path(exec: &str) -> Option<PathBuf> {
|
fn lookup_in_path(exec: &str) -> Option<Utf8PathBuf> {
|
||||||
let paths = env::var_os("PATH").unwrap_or_default();
|
let paths = env::var_os("PATH").unwrap_or_default();
|
||||||
env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe_for_binary)
|
env::split_paths(&paths)
|
||||||
|
.map(|path| path.join(exec))
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.map(Utf8PathBuf::try_from)
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.find_map(probe_for_binary)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probe_for_binary(path: PathBuf) -> Option<PathBuf> {
|
pub fn probe_for_binary(path: Utf8PathBuf) -> Option<Utf8PathBuf> {
|
||||||
let with_extension = match env::consts::EXE_EXTENSION {
|
let with_extension = match env::consts::EXE_EXTENSION {
|
||||||
"" => None,
|
"" => None,
|
||||||
it => Some(path.with_extension(it)),
|
it => Some(path.with_extension(it)),
|
||||||
|
|
|
@ -13,7 +13,7 @@ use std::fs;
|
||||||
|
|
||||||
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
|
||||||
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf, Utf8Path};
|
||||||
use vfs::loader;
|
use vfs::loader;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ impl NotifyActor {
|
||||||
if !entry.file_type().is_dir() {
|
if !entry.file_type().is_dir() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let path = AbsPath::assert(entry.path());
|
let path = AbsPath::assert(Utf8Path::from_path(entry.path()).unwrap());
|
||||||
root == path
|
root == path
|
||||||
|| dirs.exclude.iter().chain(&dirs.include).all(|it| it != path)
|
|| dirs.exclude.iter().chain(&dirs.include).all(|it| it != path)
|
||||||
});
|
});
|
||||||
|
@ -214,7 +214,7 @@ impl NotifyActor {
|
||||||
let depth = entry.depth();
|
let depth = entry.depth();
|
||||||
let is_dir = entry.file_type().is_dir();
|
let is_dir = entry.file_type().is_dir();
|
||||||
let is_file = entry.file_type().is_file();
|
let is_file = entry.file_type().is_file();
|
||||||
let abs_path = AbsPathBuf::assert(entry.into_path());
|
let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap();
|
||||||
if depth < 2 && is_dir {
|
if depth < 2 && is_dir {
|
||||||
self.send(make_message(abs_path.clone()));
|
self.send(make_message(abs_path.clone()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,7 +326,7 @@ impl VirtualPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_prefix(&self, base: &VirtualPath) -> Option<&RelPath> {
|
fn strip_prefix(&self, base: &VirtualPath) -> Option<&RelPath> {
|
||||||
<_ as AsRef<std::path::Path>>::as_ref(&self.0)
|
<_ as AsRef<paths::Utf8Path>>::as_ref(&self.0)
|
||||||
.strip_prefix(&base.0)
|
.strip_prefix(&base.0)
|
||||||
.ok()
|
.ok()
|
||||||
.map(RelPath::new_unchecked)
|
.map(RelPath::new_unchecked)
|
||||||
|
|
Loading…
Reference in a new issue