Skip to content

Commit 4a287d2

Browse files
committed
Implement invocation strategy config for checkOnSave
Note that due to how cargo works, none of the modes currently work for r-a
1 parent 7e2c41d commit 4a287d2

File tree

5 files changed

+127
-34
lines changed

5 files changed

+127
-34
lines changed

crates/flycheck/src/lib.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use std::{
88
fmt, io,
9+
path::Path,
910
process::{ChildStderr, ChildStdout, Command, Stdio},
1011
time::Duration,
1112
};
@@ -21,6 +22,14 @@ pub use cargo_metadata::diagnostic::{
2122
DiagnosticSpanMacroExpansion,
2223
};
2324

25+
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
26+
pub enum InvocationStrategy {
27+
OnceInRoot,
28+
PerWorkspaceWithManifestPath,
29+
#[default]
30+
PerWorkspace,
31+
}
32+
2433
#[derive(Clone, Debug, PartialEq, Eq)]
2534
pub enum FlycheckConfig {
2635
CargoCommand {
@@ -32,11 +41,13 @@ pub enum FlycheckConfig {
3241
features: Vec<String>,
3342
extra_args: Vec<String>,
3443
extra_env: FxHashMap<String, String>,
44+
invocation_strategy: InvocationStrategy,
3545
},
3646
CustomCommand {
3747
command: String,
3848
args: Vec<String>,
3949
extra_env: FxHashMap<String, String>,
50+
invocation_strategy: InvocationStrategy,
4051
},
4152
}
4253

@@ -136,7 +147,9 @@ enum Restart {
136147
No,
137148
}
138149

150+
/// A [`FlycheckActor`] is a single check instance of a workspace.
139151
struct FlycheckActor {
152+
/// The workspace id of this flycheck instance.
140153
id: usize,
141154
sender: Box<dyn Fn(Message) + Send>,
142155
config: FlycheckConfig,
@@ -164,9 +177,11 @@ impl FlycheckActor {
164177
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
165178
FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
166179
}
167-
fn progress(&self, progress: Progress) {
180+
181+
fn report_progress(&self, progress: Progress) {
168182
self.send(Message::Progress { id: self.id, progress });
169183
}
184+
170185
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
171186
let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
172187
if let Ok(msg) = inbox.try_recv() {
@@ -178,6 +193,7 @@ impl FlycheckActor {
178193
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
179194
}
180195
}
196+
181197
fn run(mut self, inbox: Receiver<Restart>) {
182198
'event: while let Some(event) = self.next_event(&inbox) {
183199
match event {
@@ -194,7 +210,20 @@ impl FlycheckActor {
194210
}
195211
}
196212

197-
let command = self.check_command();
213+
let mut command = self.check_command();
214+
let invocation_strategy = self.invocation_strategy();
215+
match invocation_strategy {
216+
InvocationStrategy::OnceInRoot => (),
217+
InvocationStrategy::PerWorkspaceWithManifestPath => {
218+
command.arg("--manifest-path");
219+
command.arg(<_ as AsRef<Path>>::as_ref(
220+
&self.workspace_root.join("Cargo.toml"),
221+
));
222+
}
223+
InvocationStrategy::PerWorkspace => {
224+
command.current_dir(&self.workspace_root);
225+
}
226+
}
198227
tracing::debug!(?command, "will restart flycheck");
199228
match CargoHandle::spawn(command) {
200229
Ok(cargo_handle) => {
@@ -203,10 +232,10 @@ impl FlycheckActor {
203232
"did restart flycheck"
204233
);
205234
self.cargo_handle = Some(cargo_handle);
206-
self.progress(Progress::DidStart);
235+
self.report_progress(Progress::DidStart);
207236
}
208237
Err(error) => {
209-
self.progress(Progress::DidFailToRestart(format!(
238+
self.report_progress(Progress::DidFailToRestart(format!(
210239
"Failed to run the following command: {:?} error={}",
211240
self.check_command(),
212241
error
@@ -226,11 +255,11 @@ impl FlycheckActor {
226255
self.check_command()
227256
);
228257
}
229-
self.progress(Progress::DidFinish(res));
258+
self.report_progress(Progress::DidFinish(res));
230259
}
231260
Event::CheckEvent(Some(message)) => match message {
232261
CargoMessage::CompilerArtifact(msg) => {
233-
self.progress(Progress::DidCheckCrate(msg.target.name));
262+
self.report_progress(Progress::DidCheckCrate(msg.target.name));
234263
}
235264

236265
CargoMessage::Diagnostic(msg) => {
@@ -254,7 +283,14 @@ impl FlycheckActor {
254283
"did cancel flycheck"
255284
);
256285
cargo_handle.cancel();
257-
self.progress(Progress::DidCancel);
286+
self.report_progress(Progress::DidCancel);
287+
}
288+
}
289+
290+
fn invocation_strategy(&self) -> InvocationStrategy {
291+
match self.config {
292+
FlycheckConfig::CargoCommand { invocation_strategy, .. }
293+
| FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
258294
}
259295
}
260296

@@ -269,6 +305,7 @@ impl FlycheckActor {
269305
extra_args,
270306
features,
271307
extra_env,
308+
invocation_strategy: _,
272309
} => {
273310
let mut cmd = Command::new(toolchain::cargo());
274311
cmd.arg(command);
@@ -297,7 +334,7 @@ impl FlycheckActor {
297334
cmd.envs(extra_env);
298335
cmd
299336
}
300-
FlycheckConfig::CustomCommand { command, args, extra_env } => {
337+
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => {
301338
let mut cmd = Command::new(command);
302339
cmd.args(args);
303340
cmd.envs(extra_env);

crates/rust-analyzer/src/config.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ config_data! {
130130
///
131131
/// Set to `"all"` to pass `--all-features` to Cargo.
132132
checkOnSave_features: Option<CargoFeaturesDef> = "null",
133+
/// Specifies the invocation strategy to use when running the checkOnSave command.
134+
/// If `per_workspace_with_manifest_path` is set, the command will be executed for each
135+
/// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
136+
/// the command will be executed from the project root.
137+
/// If `per_workspace` is set, the command will be executed for each workspace and the
138+
/// command will be executed from the corresponding workspace root.
139+
/// If `once_in_root` is set, the command will be executed once in the project root.
140+
checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
133141
/// Whether to pass `--no-default-features` to Cargo. Defaults to
134142
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
135143
checkOnSave_noDefaultFeatures: Option<bool> = "null",
@@ -1094,6 +1102,13 @@ impl Config {
10941102
if !self.data.checkOnSave_enable {
10951103
return None;
10961104
}
1105+
let invocation_strategy = match self.data.cargo_buildScripts_invocationStrategy {
1106+
InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot,
1107+
InvocationStrategy::PerWorkspaceWithManifestPath => {
1108+
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
1109+
}
1110+
InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace,
1111+
};
10971112
let flycheck_config = match &self.data.checkOnSave_overrideCommand {
10981113
Some(args) if !args.is_empty() => {
10991114
let mut args = args.clone();
@@ -1102,6 +1117,7 @@ impl Config {
11021117
command,
11031118
args,
11041119
extra_env: self.check_on_save_extra_env(),
1120+
invocation_strategy,
11051121
}
11061122
}
11071123
Some(_) | None => FlycheckConfig::CargoCommand {
@@ -1131,6 +1147,7 @@ impl Config {
11311147
},
11321148
extra_args: self.data.checkOnSave_extraArgs.clone(),
11331149
extra_env: self.check_on_save_extra_env(),
1150+
invocation_strategy,
11341151
},
11351152
};
11361153
Some(flycheck_config)

crates/rust-analyzer/src/reload.rs

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -473,32 +473,45 @@ impl GlobalState {
473473
};
474474

475475
let sender = self.flycheck_sender.clone();
476-
self.flycheck = self
477-
.workspaces
478-
.iter()
479-
.enumerate()
480-
.filter_map(|(id, w)| match w {
481-
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
482-
ProjectWorkspace::Json { project, .. } => {
483-
// Enable flychecks for json projects if a custom flycheck command was supplied
484-
// in the workspace configuration.
485-
match config {
486-
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
487-
_ => None,
488-
}
489-
}
490-
ProjectWorkspace::DetachedFiles { .. } => None,
491-
})
492-
.map(|(id, root)| {
493-
let sender = sender.clone();
494-
FlycheckHandle::spawn(
495-
id,
496-
Box::new(move |msg| sender.send(msg).unwrap()),
497-
config.clone(),
498-
root.to_path_buf(),
499-
)
500-
})
501-
.collect();
476+
let (FlycheckConfig::CargoCommand { invocation_strategy, .. }
477+
| FlycheckConfig::CustomCommand { invocation_strategy, .. }) = config;
478+
479+
self.flycheck = match invocation_strategy {
480+
flycheck::InvocationStrategy::OnceInRoot => vec![FlycheckHandle::spawn(
481+
0,
482+
Box::new(move |msg| sender.send(msg).unwrap()),
483+
config.clone(),
484+
self.config.root_path().clone(),
485+
)],
486+
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
487+
| flycheck::InvocationStrategy::PerWorkspace => {
488+
self.workspaces
489+
.iter()
490+
.enumerate()
491+
.filter_map(|(id, w)| match w {
492+
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
493+
ProjectWorkspace::Json { project, .. } => {
494+
// Enable flychecks for json projects if a custom flycheck command was supplied
495+
// in the workspace configuration.
496+
match config {
497+
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
498+
_ => None,
499+
}
500+
}
501+
ProjectWorkspace::DetachedFiles { .. } => None,
502+
})
503+
.map(|(id, root)| {
504+
let sender = sender.clone();
505+
FlycheckHandle::spawn(
506+
id,
507+
Box::new(move |msg| sender.send(msg).unwrap()),
508+
config.clone(),
509+
root.to_path_buf(),
510+
)
511+
})
512+
.collect()
513+
}
514+
};
502515
}
503516
}
504517

docs/user/generated_config.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ List of features to activate. Defaults to
129129

130130
Set to `"all"` to pass `--all-features` to Cargo.
131131
--
132+
[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`)::
133+
+
134+
--
135+
Specifies the invocation strategy to use when running the checkOnSave command.
136+
If `per_workspace_with_manifest_path` is set, the command will be executed for each
137+
workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
138+
the command will be executed from the project root.
139+
If `per_workspace` is set, the command will be executed for each workspace and the
140+
command will be executed from the corresponding workspace root.
141+
If `once_in_root` is set, the command will be executed once in the project root.
142+
--
132143
[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
133144
+
134145
--

editors/code/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,21 @@
561561
}
562562
]
563563
},
564+
"rust-analyzer.checkOnSave.invocationStrategy": {
565+
"markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace_with_manifest_path` is set, the command will be executed for each\nworkspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and\nthe command will be executed from the project root.\nIf `per_workspace` is set, the command will be executed for each workspace and the\ncommand will be executed from the corresponding workspace root.\nIf `once_in_root` is set, the command will be executed once in the project root.",
566+
"default": "per_workspace",
567+
"type": "string",
568+
"enum": [
569+
"per_workspace",
570+
"per_workspace_with_manifest_path",
571+
"once_in_root"
572+
],
573+
"enumDescriptions": [
574+
"The command will be executed for each workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and the command will be executed from the project root.",
575+
"The command will be executed for each workspace and the command will be executed from the corresponding workspace root.",
576+
"The command will be executed once in the project root."
577+
]
578+
},
564579
"rust-analyzer.checkOnSave.noDefaultFeatures": {
565580
"markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
566581
"default": null,

0 commit comments

Comments
 (0)