Skip to content

Commit c104f0e

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 3af6708 commit c104f0e

File tree

5 files changed

+128
-36
lines changed

5 files changed

+128
-36
lines changed

crates/flycheck/src/lib.rs

Lines changed: 46 additions & 9 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
};
@@ -20,6 +21,14 @@ pub use cargo_metadata::diagnostic::{
2021
DiagnosticSpanMacroExpansion,
2122
};
2223

24+
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
25+
pub enum InvocationStrategy {
26+
OnceInRoot,
27+
PerWorkspaceWithManifestPath,
28+
#[default]
29+
PerWorkspace,
30+
}
31+
2332
#[derive(Clone, Debug, PartialEq, Eq)]
2433
pub enum FlycheckConfig {
2534
CargoCommand {
@@ -30,18 +39,20 @@ pub enum FlycheckConfig {
3039
all_features: bool,
3140
features: Vec<String>,
3241
extra_args: Vec<String>,
42+
invocation_strategy: InvocationStrategy,
3343
},
3444
CustomCommand {
3545
command: String,
3646
args: Vec<String>,
47+
invocation_strategy: InvocationStrategy,
3748
},
3849
}
3950

4051
impl fmt::Display for FlycheckConfig {
4152
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4253
match self {
4354
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
44-
FlycheckConfig::CustomCommand { command, args } => {
55+
FlycheckConfig::CustomCommand { command, args, .. } => {
4556
write!(f, "{} {}", command, args.join(" "))
4657
}
4758
}
@@ -133,7 +144,9 @@ enum Restart {
133144
No,
134145
}
135146

147+
/// A [`FlycheckActor`] is a single check instance of a workspace.
136148
struct FlycheckActor {
149+
/// The workspace id of this flycheck instance.
137150
id: usize,
138151
sender: Box<dyn Fn(Message) + Send>,
139152
config: FlycheckConfig,
@@ -161,16 +174,19 @@ impl FlycheckActor {
161174
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
162175
FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
163176
}
164-
fn progress(&self, progress: Progress) {
177+
178+
fn report_progress(&self, progress: Progress) {
165179
self.send(Message::Progress { id: self.id, progress });
166180
}
181+
167182
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
168183
let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
169184
select! {
170185
recv(inbox) -> msg => msg.ok().map(Event::Restart),
171186
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
172187
}
173188
}
189+
174190
fn run(mut self, inbox: Receiver<Restart>) {
175191
while let Some(event) = self.next_event(&inbox) {
176192
match event {
@@ -182,7 +198,20 @@ impl FlycheckActor {
182198
self.cancel_check_process();
183199
while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}
184200

185-
let command = self.check_command();
201+
let mut command = self.check_command();
202+
let invocation_strategy = self.invocation_strategy();
203+
match invocation_strategy {
204+
InvocationStrategy::OnceInRoot => (),
205+
InvocationStrategy::PerWorkspaceWithManifestPath => {
206+
command.arg("--manifest-path");
207+
command.arg(<_ as AsRef<Path>>::as_ref(
208+
&self.workspace_root.join("Cargo.toml"),
209+
));
210+
}
211+
InvocationStrategy::PerWorkspace => {
212+
command.current_dir(&self.workspace_root);
213+
}
214+
}
186215
tracing::debug!(?command, "will restart flycheck");
187216
match CargoHandle::spawn(command) {
188217
Ok(cargo_handle) => {
@@ -191,10 +220,10 @@ impl FlycheckActor {
191220
"did restart flycheck"
192221
);
193222
self.cargo_handle = Some(cargo_handle);
194-
self.progress(Progress::DidStart);
223+
self.report_progress(Progress::DidStart);
195224
}
196225
Err(error) => {
197-
self.progress(Progress::DidFailToRestart(format!(
226+
self.report_progress(Progress::DidFailToRestart(format!(
198227
"Failed to run the following command: {:?} error={}",
199228
self.check_command(),
200229
error
@@ -214,11 +243,11 @@ impl FlycheckActor {
214243
self.check_command()
215244
);
216245
}
217-
self.progress(Progress::DidFinish(res));
246+
self.report_progress(Progress::DidFinish(res));
218247
}
219248
Event::CheckEvent(Some(message)) => match message {
220249
CargoMessage::CompilerArtifact(msg) => {
221-
self.progress(Progress::DidCheckCrate(msg.target.name));
250+
self.report_progress(Progress::DidCheckCrate(msg.target.name));
222251
}
223252

224253
CargoMessage::Diagnostic(msg) => {
@@ -242,7 +271,14 @@ impl FlycheckActor {
242271
"did cancel flycheck"
243272
);
244273
cargo_handle.cancel();
245-
self.progress(Progress::DidCancel);
274+
self.report_progress(Progress::DidCancel);
275+
}
276+
}
277+
278+
fn invocation_strategy(&self) -> InvocationStrategy {
279+
match self.config {
280+
FlycheckConfig::CargoCommand { invocation_strategy, .. }
281+
| FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
246282
}
247283
}
248284

@@ -256,6 +292,7 @@ impl FlycheckActor {
256292
all_features,
257293
extra_args,
258294
features,
295+
..
259296
} => {
260297
let mut cmd = Command::new(toolchain::cargo());
261298
cmd.arg(command);
@@ -283,7 +320,7 @@ impl FlycheckActor {
283320
cmd.args(extra_args);
284321
cmd
285322
}
286-
FlycheckConfig::CustomCommand { command, args } => {
323+
FlycheckConfig::CustomCommand { command, args, .. } => {
287324
let mut cmd = Command::new(command);
288325
cmd.args(args);
289326
cmd

crates/rust-analyzer/src/config.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ config_data! {
118118
///
119119
/// Set to `"all"` to pass `--all-features` to Cargo.
120120
checkOnSave_features: Option<CargoFeatures> = "null",
121+
/// Specifies the invocation strategy to use when running the checkOnSave command.
122+
/// If `per_workspace_with_manifest_path` is set, the command will be executed for each
123+
/// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
124+
/// the command will be executed from the project root.
125+
/// If `per_workspace` is set, the command will be executed for each workspace and the
126+
/// command will be executed from the corresponding workspace root.
127+
/// If `once_in_root` is set, the command will be executed once in the project root.
128+
checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
121129
/// Whether to pass `--no-default-features` to Cargo. Defaults to
122130
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
123131
checkOnSave_noDefaultFeatures: Option<bool> = "null",
@@ -1061,11 +1069,18 @@ impl Config {
10611069
if !self.data.checkOnSave_enable {
10621070
return None;
10631071
}
1072+
let invocation_strategy = match self.data.cargo_buildScripts_invocationStrategy {
1073+
InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot,
1074+
InvocationStrategy::PerWorkspaceWithManifestPath => {
1075+
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
1076+
}
1077+
InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace,
1078+
};
10641079
let flycheck_config = match &self.data.checkOnSave_overrideCommand {
10651080
Some(args) if !args.is_empty() => {
10661081
let mut args = args.clone();
10671082
let command = args.remove(0);
1068-
FlycheckConfig::CustomCommand { command, args }
1083+
FlycheckConfig::CustomCommand { command, args, invocation_strategy }
10691084
}
10701085
Some(_) | None => FlycheckConfig::CargoCommand {
10711086
command: self.data.checkOnSave_command.clone(),
@@ -1093,6 +1108,7 @@ impl Config {
10931108
CargoFeatures::Listed(it) => it,
10941109
},
10951110
extra_args: self.data.checkOnSave_extraArgs.clone(),
1111+
invocation_strategy,
10961112
},
10971113
};
10981114
Some(flycheck_config)

crates/rust-analyzer/src/reload.rs

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

461461
let sender = self.flycheck_sender.clone();
462-
self.flycheck = self
463-
.workspaces
464-
.iter()
465-
.enumerate()
466-
.filter_map(|(id, w)| match w {
467-
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
468-
ProjectWorkspace::Json { project, .. } => {
469-
// Enable flychecks for json projects if a custom flycheck command was supplied
470-
// in the workspace configuration.
471-
match config {
472-
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
473-
_ => None,
474-
}
475-
}
476-
ProjectWorkspace::DetachedFiles { .. } => None,
477-
})
478-
.map(|(id, root)| {
479-
let sender = sender.clone();
480-
FlycheckHandle::spawn(
481-
id,
482-
Box::new(move |msg| sender.send(msg).unwrap()),
483-
config.clone(),
484-
root.to_path_buf(),
485-
)
486-
})
487-
.collect();
462+
let (FlycheckConfig::CargoCommand { invocation_strategy, .. }
463+
| FlycheckConfig::CustomCommand { invocation_strategy, .. }) = config;
464+
465+
self.flycheck = match invocation_strategy {
466+
flycheck::InvocationStrategy::OnceInRoot => vec![FlycheckHandle::spawn(
467+
0,
468+
Box::new(move |msg| sender.send(msg).unwrap()),
469+
config.clone(),
470+
self.config.root_path().clone(),
471+
)],
472+
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
473+
| flycheck::InvocationStrategy::PerWorkspace => {
474+
self.workspaces
475+
.iter()
476+
.enumerate()
477+
.filter_map(|(id, w)| match w {
478+
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
479+
ProjectWorkspace::Json { project, .. } => {
480+
// Enable flychecks for json projects if a custom flycheck command was supplied
481+
// in the workspace configuration.
482+
match config {
483+
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
484+
_ => None,
485+
}
486+
}
487+
ProjectWorkspace::DetachedFiles { .. } => None,
488+
})
489+
.map(|(id, root)| {
490+
let sender = sender.clone();
491+
FlycheckHandle::spawn(
492+
id,
493+
Box::new(move |msg| sender.send(msg).unwrap()),
494+
config.clone(),
495+
root.to_path_buf(),
496+
)
497+
})
498+
.collect()
499+
}
500+
};
488501
}
489502
}
490503

docs/user/generated_config.adoc

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

113113
Set to `"all"` to pass `--all-features` to Cargo.
114114
--
115+
[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`)::
116+
+
117+
--
118+
Specifies the invocation strategy to use when running the checkOnSave command.
119+
If `per_workspace_with_manifest_path` is set, the command will be executed for each
120+
workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
121+
the command will be executed from the project root.
122+
If `per_workspace` is set, the command will be executed for each workspace and the
123+
command will be executed from the corresponding workspace root.
124+
If `once_in_root` is set, the command will be executed once in the project root.
125+
--
115126
[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
116127
+
117128
--

editors/code/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,21 @@
548548
}
549549
]
550550
},
551+
"rust-analyzer.checkOnSave.invocationStrategy": {
552+
"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.",
553+
"default": "per_workspace",
554+
"type": "string",
555+
"enum": [
556+
"per_workspace",
557+
"per_workspace_with_manifest_path",
558+
"once_in_root"
559+
],
560+
"enumDescriptions": [
561+
"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.",
562+
"The command will be executed for each workspace and the command will be executed from the corresponding workspace root.",
563+
"The command will be executed once in the project root."
564+
]
565+
},
551566
"rust-analyzer.checkOnSave.noDefaultFeatures": {
552567
"markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
553568
"default": null,

0 commit comments

Comments
 (0)