Auto merge of #17118 - Veykril:linked-rust-files, r=Veykril

feat: Allow rust files to be used linkedProjects

With this, script files become more usable as the user can at least add them manually to the linked projects, allowing them to be used "on the (manual) fly" without having to open a separate vscode window that only has files open and no folder.

Also makes build scripts work for them (though no proc-macros, for some reason the dylib field is not populated in the output)
This commit is contained in:
bors 2024-04-21 14:51:51 +00:00
commit f512a5e95c
21 changed files with 203 additions and 180 deletions

View file

@ -51,6 +51,7 @@ pub trait FileLoader {
/// Text of the file.
fn file_text(&self, file_id: FileId) -> Arc<str>;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
/// Crates whose root's source root is the same as the source root of `file_id`
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>;
}
@ -104,6 +105,7 @@ pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
/// Crates whose root fool is in `id`.
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
}

View file

@ -121,7 +121,7 @@ impl SourceToDefCtx<'_, '_> {
let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered();
let mut mods = SmallVec::new();
for &crate_id in self.db.relevant_crates(file).iter() {
// FIXME: inner items
// Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = self.db.crate_def_map(crate_id);
mods.extend(
crate_def_map
@ -129,6 +129,9 @@ impl SourceToDefCtx<'_, '_> {
.map(|local_id| crate_def_map.module_id(local_id)),
)
}
if mods.is_empty() {
// FIXME: detached file
}
mods
}

View file

@ -1,6 +1,6 @@
//! rust-analyzer is lazy and doesn't compute anything unless asked. This
//! sometimes is counter productive when, for example, the first goto definition
//! request takes longer to compute. This modules implemented prepopulation of
//! request takes longer to compute. This module implements prepopulation of
//! various caches, it's not really advanced at the moment.
mod topologic_sort;

View file

@ -24,7 +24,7 @@ use toolchain::Tool;
use crate::{
cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
InvocationStrategy, Package, Sysroot, TargetKind,
InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind,
};
/// Output of the build script and proc-macro building steps for a workspace.
@ -63,7 +63,7 @@ impl WorkspaceBuildScripts {
fn build_command(
config: &CargoConfig,
allowed_features: &FxHashSet<String>,
workspace_root: &AbsPathBuf,
manifest_path: &ManifestPath,
sysroot: Option<&Sysroot>,
) -> io::Result<Command> {
let mut cmd = match config.run_build_script_command.as_deref() {
@ -79,7 +79,7 @@ impl WorkspaceBuildScripts {
cmd.args(&config.extra_args);
cmd.arg("--manifest-path");
cmd.arg(workspace_root.join("Cargo.toml"));
cmd.arg(manifest_path.as_ref());
if let Some(target_dir) = &config.target_dir {
cmd.arg("--target-dir").arg(target_dir);
@ -116,6 +116,10 @@ impl WorkspaceBuildScripts {
}
}
if manifest_path.extension().map_or(false, |ext| ext == "rs") {
cmd.arg("-Zscript");
}
cmd
}
};
@ -152,37 +156,12 @@ impl WorkspaceBuildScripts {
.as_ref();
let allowed_features = workspace.workspace_features();
match Self::run_per_ws(
Self::build_command(
config,
&allowed_features,
&workspace.workspace_root().to_path_buf(),
sysroot,
)?,
workspace,
current_dir,
progress,
) {
Ok(WorkspaceBuildScripts { error: Some(error), .. })
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) =>
{
// building build scripts failed, attempt to build with --keep-going so
// that we potentially get more build data
let mut cmd = Self::build_command(
config,
&allowed_features,
&workspace.workspace_root().to_path_buf(),
sysroot,
)?;
cmd.args(["--keep-going"]);
let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
res.error = Some(error);
Ok(res)
}
res => res,
let mut cmd =
Self::build_command(config, &allowed_features, workspace.manifest_path(), sysroot)?;
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) {
cmd.args(["--keep-going"]);
}
Self::run_per_ws(cmd, workspace, current_dir, progress)
}
/// Runs the build scripts by invoking the configured command *once*.
@ -204,7 +183,13 @@ impl WorkspaceBuildScripts {
))
}
};
let cmd = Self::build_command(config, &Default::default(), workspace_root, None)?;
let cmd = Self::build_command(
config,
&Default::default(),
// This is not gonna be used anyways, so just construct a dummy here
&ManifestPath::try_from(workspace_root.clone()).unwrap(),
None,
)?;
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.

View file

@ -32,6 +32,7 @@ pub struct CargoWorkspace {
targets: Arena<TargetData>,
workspace_root: AbsPathBuf,
target_directory: AbsPathBuf,
manifest_path: ManifestPath,
}
impl ops::Index<Package> for CargoWorkspace {
@ -334,7 +335,7 @@ impl CargoWorkspace {
.with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))
}
pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
pub fn new(mut meta: cargo_metadata::Metadata, manifest_path: ManifestPath) -> CargoWorkspace {
let mut pkg_by_id = FxHashMap::default();
let mut packages = Arena::default();
let mut targets = Arena::default();
@ -448,7 +449,7 @@ impl CargoWorkspace {
let target_directory = AbsPathBuf::assert(meta.target_directory);
CargoWorkspace { packages, targets, workspace_root, target_directory }
CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path }
}
pub fn packages(&self) -> impl ExactSizeIterator<Item = Package> + '_ {
@ -466,6 +467,10 @@ impl CargoWorkspace {
&self.workspace_root
}
pub fn manifest_path(&self) -> &ManifestPath {
&self.manifest_path
}
pub fn target_directory(&self) -> &AbsPath {
&self.target_directory
}

View file

@ -54,11 +54,13 @@ pub use crate::{
sysroot::Sysroot,
workspace::{FileLoader, PackageRoot, ProjectWorkspace},
};
pub use cargo_metadata::Metadata;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ProjectManifest {
ProjectJson(ManifestPath),
CargoToml(ManifestPath),
CargoScript(ManifestPath),
}
impl ProjectManifest {
@ -71,7 +73,10 @@ impl ProjectManifest {
if path.file_name().unwrap_or_default() == "Cargo.toml" {
return Ok(ProjectManifest::CargoToml(path));
}
bail!("project root must point to Cargo.toml or rust-project.json: {path}");
if path.extension().unwrap_or_default() == "rs" {
return Ok(ProjectManifest::CargoScript(path));
}
bail!("project root must point to a Cargo.toml, rust-project.json or <script>.rs file: {path}");
}
pub fn discover_single(path: &AbsPath) -> anyhow::Result<ProjectManifest> {
@ -146,15 +151,19 @@ impl ProjectManifest {
res.sort();
res
}
pub fn manifest_path(&self) -> &ManifestPath {
match self {
ProjectManifest::ProjectJson(it)
| ProjectManifest::CargoToml(it)
| ProjectManifest::CargoScript(it) => it,
}
}
}
impl fmt::Display for ProjectManifest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProjectManifest::ProjectJson(it) | ProjectManifest::CargoToml(it) => {
fmt::Display::fmt(&it, f)
}
}
fmt::Display::fmt(self.manifest_path(), f)
}
}

View file

@ -1,5 +1,5 @@
//! See [`ManifestPath`].
use std::{fmt, ops, path::Path};
use std::{borrow::Borrow, fmt, ops};
use paths::{AbsPath, AbsPathBuf};
@ -54,8 +54,14 @@ impl ops::Deref for ManifestPath {
}
}
impl AsRef<Path> for ManifestPath {
fn as_ref(&self) -> &Path {
impl AsRef<AbsPath> for ManifestPath {
fn as_ref(&self) -> &AbsPath {
self.file.as_ref()
}
}
impl Borrow<AbsPath> for ManifestPath {
fn borrow(&self) -> &AbsPath {
self.file.borrow()
}
}

View file

@ -349,7 +349,7 @@ impl Sysroot {
.filter(|&package| RELEVANT_SYSROOT_CRATES.contains(&&*package.name))
.map(|package| package.id.clone())
.collect();
let cargo_workspace = CargoWorkspace::new(res);
let cargo_workspace = CargoWorkspace::new(res, sysroot_cargo_toml);
Some(Sysroot {
root: sysroot_dir.clone(),
src_root: Some(Ok(sysroot_src_dir.clone())),
@ -368,7 +368,7 @@ impl Sysroot {
.into_iter()
.map(|it| sysroot_src_dir.join(it))
.filter_map(|it| ManifestPath::try_from(it).ok())
.find(|it| fs::metadata(it).is_ok());
.find(|it| fs::metadata(it.as_ref()).is_ok());
if let Some(root) = root {
stitched.crates.alloc(SysrootCrateData {
@ -468,7 +468,7 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option<ManifestPath> {
let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
let rustc_src = ManifestPath::try_from(rustc_src).ok()?;
tracing::debug!("checking for rustc source code: {rustc_src}");
if fs::metadata(&rustc_src).is_ok() {
if fs::metadata(rustc_src.as_ref()).is_ok() {
Some(rustc_src)
} else {
None

View file

@ -1,6 +1,7 @@
use std::ops::Deref;
use base_db::{CrateGraph, FileId, ProcMacroPaths};
use cargo_metadata::Metadata;
use cfg::{CfgAtom, CfgDiff};
use expect_test::{expect_file, ExpectFile};
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
@ -9,8 +10,8 @@ use serde::de::DeserializeOwned;
use triomphe::Arc;
use crate::{
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
WorkspaceBuildScripts,
CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace,
Sysroot, WorkspaceBuildScripts,
};
fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
@ -21,8 +22,10 @@ fn load_cargo_with_overrides(
file: &str,
cfg_overrides: CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
let meta = get_test_json_file(file);
let cargo_workspace = CargoWorkspace::new(meta);
let meta: Metadata = get_test_json_file(file);
let manifest_path =
ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
let project_workspace = ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
@ -41,8 +44,10 @@ fn load_cargo_with_fake_sysroot(
file_map: &mut FxHashMap<AbsPathBuf, FileId>,
file: &str,
) -> (CrateGraph, ProcMacroPaths) {
let meta = get_test_json_file(file);
let cargo_workspace = CargoWorkspace::new(meta);
let meta: Metadata = get_test_json_file(file);
let manifest_path =
ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
let project_workspace = ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
@ -268,9 +273,10 @@ fn smoke_test_real_sysroot_cargo() {
return;
}
let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default();
let meta = get_test_json_file("hello-world-metadata.json");
let cargo_workspace = CargoWorkspace::new(meta);
let meta: Metadata = get_test_json_file("hello-world-metadata.json");
let manifest_path =
ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
let sysroot = Ok(Sysroot::discover(
AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
&Default::default(),

View file

@ -4,7 +4,7 @@
use std::{collections::VecDeque, fmt, fs, iter, sync};
use anyhow::{format_err, Context};
use anyhow::Context;
use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId,
LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
@ -14,7 +14,6 @@ use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use span::Edition;
use stdx::always;
use toolchain::Tool;
use triomphe::Arc;
@ -101,7 +100,7 @@ pub enum ProjectWorkspace {
/// Backed by basic sysroot crates for basic completion and highlighting.
DetachedFile {
/// The file in question.
file: AbsPathBuf,
file: ManifestPath,
/// The sysroot loaded for this workspace.
sysroot: Result<Sysroot, Option<String>>,
/// Holds cfg flags for the current target. We get those by running
@ -116,7 +115,7 @@ pub enum ProjectWorkspace {
/// A set of cfg overrides for the files.
cfg_overrides: CfgOverrides,
/// Is this file a cargo script file?
cargo_script: Option<CargoWorkspace>,
cargo_script: Option<(CargoWorkspace, WorkspaceBuildScripts)>,
},
}
@ -230,7 +229,7 @@ impl ProjectWorkspace {
) -> anyhow::Result<ProjectWorkspace> {
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
let file = fs::read_to_string(project_json)
let file = fs::read_to_string(project_json.as_ref())
.with_context(|| format!("Failed to read json file {project_json}"))?;
let data = serde_json::from_str(&file)
.with_context(|| format!("Failed to deserialize json file {project_json}"))?;
@ -243,6 +242,9 @@ impl ProjectWorkspace {
&config.cfg_overrides,
)
}
ProjectManifest::CargoScript(rust_file) => {
ProjectWorkspace::load_detached_file(rust_file, config)?
}
ProjectManifest::CargoToml(cargo_toml) => {
let sysroot = match (&config.sysroot, &config.sysroot_src) {
(Some(RustLibSource::Path(path)), None) => {
@ -299,7 +301,7 @@ impl ProjectWorkspace {
progress,
) {
Ok(meta) => {
let workspace = CargoWorkspace::new(meta);
let workspace = CargoWorkspace::new(meta, cargo_toml.clone());
let buildscripts = WorkspaceBuildScripts::rustc_crates(
&workspace,
cargo_toml.parent(),
@ -355,7 +357,7 @@ impl ProjectWorkspace {
"Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
)
})?;
let cargo = CargoWorkspace::new(meta);
let cargo = CargoWorkspace::new(meta, cargo_toml.clone());
let cargo_config_extra_env =
cargo_config_env(cargo_toml, &config.extra_env, sysroot_ref);
@ -433,82 +435,71 @@ impl ProjectWorkspace {
}
}
pub fn load_detached_file(
detached_file: &ManifestPath,
config: &CargoConfig,
) -> anyhow::Result<ProjectWorkspace> {
let dir = detached_file.parent();
let sysroot = match &config.sysroot {
Some(RustLibSource::Path(path)) => {
Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
.map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
}
Some(RustLibSource::Discover) => Sysroot::discover(
dir,
&config.extra_env,
config.sysroot_query_metadata,
)
.map_err(|e| {
Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}"))
}),
None => Err(None),
};
let sysroot_ref = sysroot.as_ref().ok();
let toolchain =
match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ")
{
Ok(it) => it,
Err(e) => {
tracing::error!("{e}");
None
}
};
let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
let data_layout = target_data_layout::get(
RustcDataLayoutConfig::Rustc(sysroot_ref),
None,
&config.extra_env,
);
let cargo_script =
CargoWorkspace::fetch_metadata(detached_file, dir, config, sysroot_ref, &|_| ())
.ok()
.map(|ws| {
(
CargoWorkspace::new(ws, detached_file.clone()),
WorkspaceBuildScripts::default(),
)
});
Ok(ProjectWorkspace::DetachedFile {
file: detached_file.to_owned(),
sysroot,
rustc_cfg,
toolchain,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cfg_overrides: config.cfg_overrides.clone(),
cargo_script,
})
}
pub fn load_detached_files(
detached_files: Vec<AbsPathBuf>,
detached_files: Vec<ManifestPath>,
config: &CargoConfig,
) -> Vec<anyhow::Result<ProjectWorkspace>> {
detached_files
.into_iter()
.map(|detached_file| {
let dir = detached_file
.parent()
.ok_or_else(|| format_err!("detached file has no parent"))?;
let sysroot = match &config.sysroot {
Some(RustLibSource::Path(path)) => {
Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
.map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
}
Some(RustLibSource::Discover) => {
Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata)
.map_err(|e| {
Some(format!(
"Failed to find sysroot for {dir}. Is rust-src installed? {e}"
))
})
}
None => Err(None),
};
let sysroot_ref = sysroot.as_ref().ok();
let toolchain = match get_toolchain_version(
dir,
sysroot_ref,
Tool::Rustc,
&config.extra_env,
"rustc ",
) {
Ok(it) => it,
Err(e) => {
tracing::error!("{e}");
None
}
};
let rustc_cfg =
rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
let data_layout = target_data_layout::get(
RustcDataLayoutConfig::Rustc(sysroot_ref),
None,
&config.extra_env,
);
let cargo_script = ManifestPath::try_from(detached_file.clone())
.ok()
.and_then(|file| {
CargoWorkspace::fetch_metadata(
&file,
file.parent(),
config,
sysroot_ref,
&|_| (),
)
.ok()
})
.map(CargoWorkspace::new);
Ok(ProjectWorkspace::DetachedFile {
file: detached_file,
sysroot,
rustc_cfg,
toolchain,
target_layout: data_layout
.map(Arc::from)
.map_err(|it| Arc::from(it.to_string())),
cfg_overrides: config.cfg_overrides.clone(),
cargo_script,
})
})
.collect()
detached_files.into_iter().map(|file| Self::load_detached_file(&file, config)).collect()
}
/// Runs the build scripts for this [`ProjectWorkspace`].
@ -519,7 +510,7 @@ impl ProjectWorkspace {
) -> anyhow::Result<WorkspaceBuildScripts> {
match self {
ProjectWorkspace::DetachedFile {
cargo_script: Some(cargo),
cargo_script: Some((cargo, _)),
toolchain,
sysroot,
..
@ -586,10 +577,11 @@ impl ProjectWorkspace {
pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
match self {
ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
_ => {
always!(bs == WorkspaceBuildScripts::default());
ProjectWorkspace::Cargo { build_scripts, .. }
| ProjectWorkspace::DetachedFile { cargo_script: Some((_, build_scripts)), .. } => {
*build_scripts = bs
}
_ => assert_eq!(bs, WorkspaceBuildScripts::default()),
}
}
@ -742,15 +734,18 @@ impl ProjectWorkspace {
ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => {
iter::once(PackageRoot {
is_local: true,
include: vec![file.clone()],
include: vec![file.as_ref().to_owned()],
exclude: Vec::new(),
})
.chain(cargo_script.iter().flat_map(|cargo| {
.chain(cargo_script.iter().flat_map(|(cargo, build_scripts)| {
cargo.packages().map(|pkg| {
let is_local = cargo[pkg].is_local;
let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
let mut include = vec![pkg_root.clone()];
let out_dir =
build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
include.extend(out_dir);
// In case target's path is manually set in Cargo.toml to be
// outside the package root, add its parent as an extra include.
@ -801,7 +796,7 @@ impl ProjectWorkspace {
ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => {
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
sysroot_package_len
+ cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len())
+ cargo_script.as_ref().map_or(1, |(cargo, _)| cargo.packages().len())
}
}
}
@ -863,7 +858,7 @@ impl ProjectWorkspace {
cfg_overrides,
cargo_script,
} => (
if let Some(cargo) = cargo_script {
if let Some((cargo, build_scripts)) = cargo_script {
cargo_to_crate_graph(
&mut |path| load(path),
None,
@ -871,7 +866,7 @@ impl ProjectWorkspace {
sysroot.as_ref().ok(),
rustc_cfg.clone(),
cfg_overrides,
&WorkspaceBuildScripts::default(),
build_scripts,
)
} else {
detached_file_to_crate_graph(
@ -959,7 +954,7 @@ impl ProjectWorkspace {
file,
sysroot,
rustc_cfg,
cargo_script,
cargo_script: Some((cargo_script, _)),
toolchain,
target_layout,
cfg_overrides,
@ -968,7 +963,7 @@ impl ProjectWorkspace {
file: o_file,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
cargo_script: o_cargo_script,
cargo_script: Some((o_cargo_script, _)),
toolchain: o_toolchain,
target_layout: o_target_layout,
cfg_overrides: o_cfg_overrides,
@ -1294,11 +1289,11 @@ fn cargo_to_crate_graph(
fn detached_file_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
load: FileLoader<'_>,
detached_file: &AbsPathBuf,
detached_file: &ManifestPath,
sysroot: Option<&Sysroot>,
override_cfg: &CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
let _p = tracing::span!(tracing::Level::INFO, "detached_files_to_crate_graph").entered();
let _p = tracing::span!(tracing::Level::INFO, "detached_file_to_crate_graph").entered();
let mut crate_graph = CrateGraph::default();
let (public_deps, _libproc_macro) = match sysroot {
Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),

View file

@ -96,7 +96,7 @@ impl<'a> ProgressReport<'a> {
}
fn set_value(&mut self, value: f32) {
self.curr = f32::max(0.0, f32::min(1.0, value));
self.curr = value.clamp(0.0, 1.0);
}
fn clear(&mut self) {

View file

@ -10,7 +10,9 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
use itertools::Either;
use profile::StopWatch;
use project_model::target_data_layout::RustcDataLayoutConfig;
use project_model::{target_data_layout, CargoConfig, ProjectWorkspace, RustLibSource, Sysroot};
use project_model::{
target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, RustLibSource, Sysroot,
};
use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
use rustc_hash::FxHashMap;
@ -76,7 +78,7 @@ impl Tester {
);
let workspace = ProjectWorkspace::DetachedFile {
file: tmp_file,
file: ManifestPath::try_from(tmp_file).unwrap(),
sysroot,
rustc_cfg: vec![],
toolchain: None,

View file

@ -356,7 +356,8 @@ config_data! {
/// of projects.
///
/// Elements must be paths pointing to `Cargo.toml`,
/// `rust-project.json`, or JSON objects in `rust-project.json` format.
/// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
/// objects in `rust-project.json` format.
linkedProjects: Vec<ManifestOrProjectJson> = vec![],
/// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
@ -1301,12 +1302,9 @@ impl Config {
self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
self.discovered_projects
.iter()
.filter(
|(ProjectManifest::ProjectJson(path)
| ProjectManifest::CargoToml(path))| {
!exclude_dirs.iter().any(|p| path.starts_with(p))
},
)
.filter(|project| {
!exclude_dirs.iter().any(|p| project.manifest_path().starts_with(p))
})
.cloned()
.map(LinkedProject::from)
.collect()

View file

@ -18,7 +18,9 @@ use parking_lot::{
RwLockWriteGuard,
};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use project_model::{
CargoWorkspace, ManifestPath, ProjectWorkspace, Target, WorkspaceBuildScripts,
};
use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc;
use vfs::{AnchoredPathBuf, ChangedFile, Vfs};
@ -125,7 +127,7 @@ pub(crate) struct GlobalState {
/// to invalidate any salsa caches.
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
pub(crate) detached_files: FxHashSet<vfs::AbsPathBuf>,
pub(crate) detached_files: FxHashSet<ManifestPath>,
// op queues
pub(crate) fetch_workspaces_queue:

View file

@ -1759,11 +1759,12 @@ pub(crate) fn handle_open_docs(
let position = from_proto::file_position(&snap, params)?;
let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws {
ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())),
ProjectWorkspace::Json { .. } => None,
ProjectWorkspace::DetachedFile { cargo_script, sysroot, .. } => {
cargo_script.as_ref().zip(Some(sysroot.as_ref().ok()))
ProjectWorkspace::Cargo { cargo, sysroot, .. }
| ProjectWorkspace::DetachedFile { cargo_script: Some((cargo, _)), sysroot, .. } => {
Some((cargo, sysroot.as_ref().ok()))
}
ProjectWorkspace::Json { .. } => None,
ProjectWorkspace::DetachedFile { .. } => None,
});
let (cargo, sysroot) = match ws_and_sysroot {

View file

@ -25,7 +25,7 @@ use ide_db::{
use itertools::Itertools;
use load_cargo::{load_proc_macro, ProjectFolders};
use proc_macro_api::ProcMacroServer;
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
use project_model::{ManifestPath, ProjectWorkspace, WorkspaceBuildScripts};
use stdx::{format_to, thread::ThreadIntent};
use triomphe::Arc;
use vfs::{AbsPath, AbsPathBuf, ChangeKind};
@ -204,7 +204,14 @@ impl GlobalState {
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
let linked_projects = self.config.linked_or_discovered_projects();
let detached_files = self.config.detached_files().to_vec();
let detached_files: Vec<_> = self
.config
.detached_files()
.iter()
.cloned()
.map(ManifestPath::try_from)
.filter_map(Result::ok)
.collect();
let cargo_config = self.config.cargo();
move |sender| {

View file

@ -1,14 +1,18 @@
use std::path::PathBuf;
use project_model::{CargoWorkspace, ProjectWorkspace, Sysroot, WorkspaceBuildScripts};
use project_model::{
CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, Sysroot, WorkspaceBuildScripts,
};
use rust_analyzer::ws_to_crate_graph;
use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;
use vfs::{AbsPathBuf, FileId};
fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace {
let meta = get_test_json_file(file);
let cargo_workspace = CargoWorkspace::new(meta);
let meta: Metadata = get_test_json_file(file);
let manifest_path =
ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),

View file

@ -150,6 +150,7 @@ use dependency2::Spam;
)
.with_config(serde_json::json!({
"cargo": { "sysroot": null },
"detachedFiles": ["/src/lib.rs"],
}))
.server()
.wait_until_workspace_is_loaded();

View file

@ -185,11 +185,7 @@ impl Project<'_> {
roots,
None,
);
// TODO: don't hardcode src/lib.rs as detached file
let mut c = self.config;
let p = tmp_dir_path.join("src/lib.rs").to_string();
c["detachedFiles"] = serde_json::json!([p]);
config.update(c).expect("invalid config");
config.update(self.config).expect("invalid config");
config.rediscover_workspaces();
Server::new(tmp_dir.keep(), config)

View file

@ -778,7 +778,8 @@ Disable project auto-discovery in favor of explicitly specified set
of projects.
Elements must be paths pointing to `Cargo.toml`,
`rust-project.json`, or JSON objects in `rust-project.json` format.
`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
objects in `rust-project.json` format.
--
[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`)::
+

View file

@ -1495,7 +1495,7 @@
"type": "boolean"
},
"rust-analyzer.linkedProjects": {
"markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.",
"markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON\nobjects in `rust-project.json` format.",
"default": [],
"type": "array",
"items": {