Skip to content

Add minimal support for cargo scripts #15456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/project-model/src/cargo_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ impl CargoWorkspace {
.collect(),
);
}
if cargo_toml.extension().is_some_and(|x| x == "rs") {
// TODO: enable `+nightly` for cargo scripts
other_options.push("-Zscript".to_owned());
}
meta.other_options(other_options);

// FIXME: Fetching metadata is a slow process, as it might require
Expand Down
2 changes: 1 addition & 1 deletion crates/project-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use crate::{
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
sysroot::Sysroot,
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
workspace::{CargoScriptTomls, CfgOverrides, PackageRoot, ProjectWorkspace},
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
Expand Down
103 changes: 95 additions & 8 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.

use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync};
use std::{collections::VecDeque, fmt, fs, io::BufRead, iter, str::FromStr, sync};

use anyhow::{format_err, Context};
use base_db::{
Expand Down Expand Up @@ -103,9 +103,54 @@ pub enum ProjectWorkspace {
rustc_cfg: Vec<CfgFlag>,
toolchain: Option<Version>,
target_layout: TargetLayoutLoadResult,
cargo_script: Option<CargoWorkspace>,
},
}

/// Tracks the cargo toml parts in cargo scripts, to detect if they
/// changed and reload workspace in that case.
pub struct CargoScriptTomls(pub FxHashMap<AbsPathBuf, String>);

impl CargoScriptTomls {
fn extract_toml_part(p: &AbsPath) -> Option<String> {
let mut r = String::new();
let f = std::fs::File::open(p).ok()?;
let f = std::io::BufReader::new(f);
let mut started = false;
for line in f.lines() {
let line = line.ok()?;
if started {
if line.trim() == "//! ```" {
return Some(r);
}
r += &line;
} else {
if line.trim() == "//! ```cargo" {
started = true;
}
}
}
None
}

pub fn track_file(&mut self, p: AbsPathBuf) {
let toml = CargoScriptTomls::extract_toml_part(&p).unwrap_or_default();
self.0.insert(p, toml);
}

pub fn need_reload(&mut self, p: &AbsPath) -> bool {
let Some(prev) = self.0.get_mut(p) else {
return false; // File is not tracked
};
let next = CargoScriptTomls::extract_toml_part(p).unwrap_or_default();
if *prev == next {
return false;
}
*prev = next;
true
}
}

impl fmt::Debug for ProjectWorkspace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Make sure this isn't too verbose.
Expand Down Expand Up @@ -159,6 +204,7 @@ impl fmt::Debug for ProjectWorkspace {
rustc_cfg,
toolchain,
target_layout,
cargo_script: _,
} => f
.debug_struct("DetachedFiles")
.field("n_files", &files.len())
Expand Down Expand Up @@ -412,6 +458,7 @@ impl ProjectWorkspace {
pub fn load_detached_files(
detached_files: Vec<AbsPathBuf>,
config: &CargoConfig,
cargo_script_tomls: &mut CargoScriptTomls,
) -> anyhow::Result<ProjectWorkspace> {
let dir = detached_files
.first()
Expand Down Expand Up @@ -450,12 +497,30 @@ impl ProjectWorkspace {
None,
&config.extra_env,
);
let cargo_toml = ManifestPath::try_from(detached_files[0].clone()).unwrap();
let meta = CargoWorkspace::fetch_metadata(
&cargo_toml,
cargo_toml.parent(),
config,
sysroot_ref,
&|_| (),
)
.with_context(|| {
format!("Failed to read Cargo metadata from Cargo.toml file {cargo_toml}")
})?;
let cargo = CargoWorkspace::new(meta);

for file in &detached_files {
cargo_script_tomls.track_file(file.clone());
}

Ok(ProjectWorkspace::DetachedFiles {
files: detached_files,
sysroot,
rustc_cfg,
toolchain,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cargo_script: Some(cargo),
})
}

Expand Down Expand Up @@ -764,13 +829,26 @@ impl ProjectWorkspace {
rustc_cfg,
toolchain: _,
target_layout: _,
cargo_script,
} => (
detached_files_to_crate_graph(
rustc_cfg.clone(),
load,
files,
sysroot.as_ref().ok(),
),
if let Some(cargo) = cargo_script {
cargo_to_crate_graph(
load,
None,
cargo,
sysroot.as_ref().ok(),
rustc_cfg.clone(),
&CfgOverrides::default(),
&WorkspaceBuildScripts::default(),
)
} else {
detached_files_to_crate_graph(
rustc_cfg.clone(),
load,
files,
sysroot.as_ref().ok(),
)
},
sysroot,
),
};
Expand Down Expand Up @@ -835,11 +913,19 @@ impl ProjectWorkspace {
&& toolchain == o_toolchain
}
(
Self::DetachedFiles { files, sysroot, rustc_cfg, toolchain, target_layout },
Self::DetachedFiles {
files,
sysroot,
rustc_cfg,
cargo_script,
toolchain,
target_layout,
},
Self::DetachedFiles {
files: o_files,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
cargo_script: o_cargo_script,
toolchain: o_toolchain,
target_layout: o_target_layout,
},
Expand All @@ -849,6 +935,7 @@ impl ProjectWorkspace {
&& rustc_cfg == o_rustc_cfg
&& toolchain == o_toolchain
&& target_layout == o_target_layout
&& cargo_script == o_cargo_script
}
_ => false,
}
Expand Down
1 change: 1 addition & 0 deletions crates/rust-analyzer/src/cli/rustc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ impl Tester {
rustc_cfg: vec![],
toolchain: None,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cargo_script: None,
};
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: false,
Expand Down
12 changes: 10 additions & 2 deletions crates/rust-analyzer/src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use parking_lot::{
RwLockWriteGuard,
};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use project_model::{
CargoScriptTomls, CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts,
};
use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc;
use vfs::{AnchoredPathBuf, ChangedFile, Vfs};
Expand Down Expand Up @@ -145,6 +147,7 @@ pub(crate) struct GlobalState {
/// this queue should run only *after* [`GlobalState::process_changes`] has
/// been called.
pub(crate) deferred_task_queue: TaskQueue,
pub(crate) cargo_script_tomls: Arc<Mutex<CargoScriptTomls>>,
}

/// An immutable snapshot of the world's state at a point in time.
Expand Down Expand Up @@ -242,6 +245,7 @@ impl GlobalState {
prime_caches_queue: OpQueue::default(),

deferred_task_queue: task_queue,
cargo_script_tomls: Arc::new(Mutex::new(CargoScriptTomls(FxHashMap::default()))),
};
// Apply any required database inputs from the config.
this.update_configuration(config);
Expand Down Expand Up @@ -324,7 +328,11 @@ impl GlobalState {
if file.is_created_or_deleted() {
workspace_structure_change.get_or_insert((path, false)).1 |=
self.crate_graph_file_dependencies.contains(vfs_path);
} else if reload::should_refresh_for_change(&path, file.kind()) {
} else if reload::should_refresh_for_change(
&path,
file.kind(),
&mut self.cargo_script_tomls.lock(),
) {
workspace_structure_change.get_or_insert((path.clone(), false));
}
}
Expand Down
6 changes: 5 additions & 1 deletion crates/rust-analyzer/src/handlers/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ pub(crate) fn handle_did_save_text_document(
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
// Re-fetch workspaces if a workspace related file has changed
if let Some(abs_path) = vfs_path.as_path() {
if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
if reload::should_refresh_for_change(
abs_path,
ChangeKind::Modify,
&mut state.cargo_script_tomls.lock(),
) {
state
.fetch_workspaces_queue
.request_op(format!("workspace vfs file change saved {abs_path}"), false);
Expand Down
14 changes: 12 additions & 2 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{CargoScriptTomls, ProjectWorkspace, WorkspaceBuildScripts};
use stdx::{format_to, thread::ThreadIntent};
use triomphe::Arc;
use vfs::{AbsPath, AbsPathBuf, ChangeKind};
Expand Down Expand Up @@ -206,6 +206,7 @@ impl GlobalState {
let linked_projects = self.config.linked_or_discovered_projects();
let detached_files = self.config.detached_files().to_vec();
let cargo_config = self.config.cargo();
let cargo_script_tomls = self.cargo_script_tomls.clone();

move |sender| {
let progress = {
Expand Down Expand Up @@ -257,6 +258,7 @@ impl GlobalState {
workspaces.push(project_model::ProjectWorkspace::load_detached_files(
detached_files,
&cargo_config,
&mut cargo_script_tomls.lock(),
));
}

Expand Down Expand Up @@ -757,7 +759,15 @@ pub fn ws_to_crate_graph(
(crate_graph, proc_macro_paths, layouts, toolchains)
}

pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
pub(crate) fn should_refresh_for_change(
path: &AbsPath,
change_kind: ChangeKind,
cargo_script_tomls: &mut CargoScriptTomls,
) -> bool {
if cargo_script_tomls.need_reload(path) {
return true;
}

const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

Expand Down
Loading
Loading