Skip to content

Commit 82a6bc1

Browse files
HKalbasiVeykril
authored andcommitted
Add minimal support for cargo scripts
1 parent d80df71 commit 82a6bc1

File tree

9 files changed

+243
-17
lines changed

9 files changed

+243
-17
lines changed

crates/project-model/src/cargo_workspace.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ impl CargoWorkspace {
287287
.collect(),
288288
);
289289
}
290+
if cargo_toml.extension().is_some_and(|x| x == "rs") {
291+
// TODO: enable `+nightly` for cargo scripts
292+
other_options.push("-Zscript".to_owned());
293+
}
290294
meta.other_options(other_options);
291295

292296
// FIXME: Fetching metadata is a slow process, as it might require

crates/project-model/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub use crate::{
5050
manifest_path::ManifestPath,
5151
project_json::{ProjectJson, ProjectJsonData},
5252
sysroot::Sysroot,
53-
workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
53+
workspace::{CargoScriptTomls, CfgOverrides, PackageRoot, ProjectWorkspace},
5454
};
5555

5656
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]

crates/project-model/src/workspace.rs

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! metadata` or `rust-project.json`) into representation stored in the salsa
33
//! database -- `CrateGraph`.
44
5-
use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync};
5+
use std::{collections::VecDeque, fmt, fs, io::BufRead, iter, str::FromStr, sync};
66

77
use anyhow::{format_err, Context};
88
use base_db::{
@@ -103,9 +103,54 @@ pub enum ProjectWorkspace {
103103
rustc_cfg: Vec<CfgFlag>,
104104
toolchain: Option<Version>,
105105
target_layout: TargetLayoutLoadResult,
106+
cargo_script: Option<CargoWorkspace>,
106107
},
107108
}
108109

110+
/// Tracks the cargo toml parts in cargo scripts, to detect if they
111+
/// changed and reload workspace in that case.
112+
pub struct CargoScriptTomls(pub FxHashMap<AbsPathBuf, String>);
113+
114+
impl CargoScriptTomls {
115+
fn extract_toml_part(p: &AbsPath) -> Option<String> {
116+
let mut r = String::new();
117+
let f = std::fs::File::open(p).ok()?;
118+
let f = std::io::BufReader::new(f);
119+
let mut started = false;
120+
for line in f.lines() {
121+
let line = line.ok()?;
122+
if started {
123+
if line.trim() == "//! ```" {
124+
return Some(r);
125+
}
126+
r += &line;
127+
} else {
128+
if line.trim() == "//! ```cargo" {
129+
started = true;
130+
}
131+
}
132+
}
133+
None
134+
}
135+
136+
pub fn track_file(&mut self, p: AbsPathBuf) {
137+
let toml = CargoScriptTomls::extract_toml_part(&p).unwrap_or_default();
138+
self.0.insert(p, toml);
139+
}
140+
141+
pub fn need_reload(&mut self, p: &AbsPath) -> bool {
142+
let Some(prev) = self.0.get_mut(p) else {
143+
return false; // File is not tracked
144+
};
145+
let next = CargoScriptTomls::extract_toml_part(p).unwrap_or_default();
146+
if *prev == next {
147+
return false;
148+
}
149+
*prev = next;
150+
true
151+
}
152+
}
153+
109154
impl fmt::Debug for ProjectWorkspace {
110155
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111156
// Make sure this isn't too verbose.
@@ -159,6 +204,7 @@ impl fmt::Debug for ProjectWorkspace {
159204
rustc_cfg,
160205
toolchain,
161206
target_layout,
207+
cargo_script: _,
162208
} => f
163209
.debug_struct("DetachedFiles")
164210
.field("n_files", &files.len())
@@ -412,6 +458,7 @@ impl ProjectWorkspace {
412458
pub fn load_detached_files(
413459
detached_files: Vec<AbsPathBuf>,
414460
config: &CargoConfig,
461+
cargo_script_tomls: &mut CargoScriptTomls,
415462
) -> anyhow::Result<ProjectWorkspace> {
416463
let dir = detached_files
417464
.first()
@@ -450,12 +497,30 @@ impl ProjectWorkspace {
450497
None,
451498
&config.extra_env,
452499
);
500+
let cargo_toml = ManifestPath::try_from(detached_files[0].clone()).unwrap();
501+
let meta = CargoWorkspace::fetch_metadata(
502+
&cargo_toml,
503+
cargo_toml.parent(),
504+
config,
505+
sysroot_ref,
506+
&|_| (),
507+
)
508+
.with_context(|| {
509+
format!("Failed to read Cargo metadata from Cargo.toml file {cargo_toml}")
510+
})?;
511+
let cargo = CargoWorkspace::new(meta);
512+
513+
for file in &detached_files {
514+
cargo_script_tomls.track_file(file.clone());
515+
}
516+
453517
Ok(ProjectWorkspace::DetachedFiles {
454518
files: detached_files,
455519
sysroot,
456520
rustc_cfg,
457521
toolchain,
458522
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
523+
cargo_script: Some(cargo),
459524
})
460525
}
461526

@@ -764,13 +829,26 @@ impl ProjectWorkspace {
764829
rustc_cfg,
765830
toolchain: _,
766831
target_layout: _,
832+
cargo_script,
767833
} => (
768-
detached_files_to_crate_graph(
769-
rustc_cfg.clone(),
770-
load,
771-
files,
772-
sysroot.as_ref().ok(),
773-
),
834+
if let Some(cargo) = cargo_script {
835+
cargo_to_crate_graph(
836+
load,
837+
None,
838+
cargo,
839+
sysroot.as_ref().ok(),
840+
rustc_cfg.clone(),
841+
&CfgOverrides::default(),
842+
&WorkspaceBuildScripts::default(),
843+
)
844+
} else {
845+
detached_files_to_crate_graph(
846+
rustc_cfg.clone(),
847+
load,
848+
files,
849+
sysroot.as_ref().ok(),
850+
)
851+
},
774852
sysroot,
775853
),
776854
};
@@ -835,11 +913,19 @@ impl ProjectWorkspace {
835913
&& toolchain == o_toolchain
836914
}
837915
(
838-
Self::DetachedFiles { files, sysroot, rustc_cfg, toolchain, target_layout },
916+
Self::DetachedFiles {
917+
files,
918+
sysroot,
919+
rustc_cfg,
920+
cargo_script,
921+
toolchain,
922+
target_layout,
923+
},
839924
Self::DetachedFiles {
840925
files: o_files,
841926
sysroot: o_sysroot,
842927
rustc_cfg: o_rustc_cfg,
928+
cargo_script: o_cargo_script,
843929
toolchain: o_toolchain,
844930
target_layout: o_target_layout,
845931
},
@@ -849,6 +935,7 @@ impl ProjectWorkspace {
849935
&& rustc_cfg == o_rustc_cfg
850936
&& toolchain == o_toolchain
851937
&& target_layout == o_target_layout
938+
&& cargo_script == o_cargo_script
852939
}
853940
_ => false,
854941
}

crates/rust-analyzer/src/cli/rustc_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ impl Tester {
8181
rustc_cfg: vec![],
8282
toolchain: None,
8383
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
84+
cargo_script: None,
8485
};
8586
let load_cargo_config = LoadCargoConfig {
8687
load_out_dirs_from_check: false,

crates/rust-analyzer/src/global_state.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ use parking_lot::{
1818
RwLockWriteGuard,
1919
};
2020
use proc_macro_api::ProcMacroServer;
21-
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
21+
use project_model::{
22+
CargoScriptTomls, CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts,
23+
};
2224
use rustc_hash::{FxHashMap, FxHashSet};
2325
use triomphe::Arc;
2426
use vfs::{AnchoredPathBuf, ChangedFile, Vfs};
@@ -145,6 +147,7 @@ pub(crate) struct GlobalState {
145147
/// this queue should run only *after* [`GlobalState::process_changes`] has
146148
/// been called.
147149
pub(crate) deferred_task_queue: TaskQueue,
150+
pub(crate) cargo_script_tomls: Arc<Mutex<CargoScriptTomls>>,
148151
}
149152

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

244247
deferred_task_queue: task_queue,
248+
cargo_script_tomls: Arc::new(Mutex::new(CargoScriptTomls(FxHashMap::default()))),
245249
};
246250
// Apply any required database inputs from the config.
247251
this.update_configuration(config);
@@ -324,7 +328,11 @@ impl GlobalState {
324328
if file.is_created_or_deleted() {
325329
workspace_structure_change.get_or_insert((path, false)).1 |=
326330
self.crate_graph_file_dependencies.contains(vfs_path);
327-
} else if reload::should_refresh_for_change(&path, file.kind()) {
331+
} else if reload::should_refresh_for_change(
332+
&path,
333+
file.kind(),
334+
&mut self.cargo_script_tomls.lock(),
335+
) {
328336
workspace_structure_change.get_or_insert((path.clone(), false));
329337
}
330338
}

crates/rust-analyzer/src/handlers/notification.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ pub(crate) fn handle_did_save_text_document(
150150
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
151151
// Re-fetch workspaces if a workspace related file has changed
152152
if let Some(abs_path) = vfs_path.as_path() {
153-
if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
153+
if reload::should_refresh_for_change(
154+
abs_path,
155+
ChangeKind::Modify,
156+
&mut state.cargo_script_tomls.lock(),
157+
) {
154158
state
155159
.fetch_workspaces_queue
156160
.request_op(format!("workspace vfs file change saved {abs_path}"), false);

crates/rust-analyzer/src/reload.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use ide_db::{
2525
use itertools::Itertools;
2626
use load_cargo::{load_proc_macro, ProjectFolders};
2727
use proc_macro_api::ProcMacroServer;
28-
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
28+
use project_model::{CargoScriptTomls, ProjectWorkspace, WorkspaceBuildScripts};
2929
use stdx::{format_to, thread::ThreadIntent};
3030
use triomphe::Arc;
3131
use vfs::{AbsPath, AbsPathBuf, ChangeKind};
@@ -206,6 +206,7 @@ impl GlobalState {
206206
let linked_projects = self.config.linked_or_discovered_projects();
207207
let detached_files = self.config.detached_files().to_vec();
208208
let cargo_config = self.config.cargo();
209+
let cargo_script_tomls = self.cargo_script_tomls.clone();
209210

210211
move |sender| {
211212
let progress = {
@@ -257,6 +258,7 @@ impl GlobalState {
257258
workspaces.push(project_model::ProjectWorkspace::load_detached_files(
258259
detached_files,
259260
&cargo_config,
261+
&mut cargo_script_tomls.lock(),
260262
));
261263
}
262264

@@ -757,7 +759,15 @@ pub fn ws_to_crate_graph(
757759
(crate_graph, proc_macro_paths, layouts, toolchains)
758760
}
759761

760-
pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
762+
pub(crate) fn should_refresh_for_change(
763+
path: &AbsPath,
764+
change_kind: ChangeKind,
765+
cargo_script_tomls: &mut CargoScriptTomls,
766+
) -> bool {
767+
if cargo_script_tomls.need_reload(path) {
768+
return true;
769+
}
770+
761771
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
762772
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
763773

0 commit comments

Comments
 (0)