Skip to content

Commit 90786f4

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 667544e commit 90786f4

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,16 +177,19 @@ 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
select! {
173188
recv(inbox) -> msg => msg.ok().map(Event::Restart),
174189
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
175190
}
176191
}
192+
177193
fn run(mut self, inbox: Receiver<Restart>) {
178194
while let Some(event) = self.next_event(&inbox) {
179195
match event {
@@ -185,7 +201,20 @@ impl FlycheckActor {
185201
self.cancel_check_process();
186202
while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}
187203

188-
let command = self.check_command();
204+
let mut command = self.check_command();
205+
let invocation_strategy = self.invocation_strategy();
206+
match invocation_strategy {
207+
InvocationStrategy::OnceInRoot => (),
208+
InvocationStrategy::PerWorkspaceWithManifestPath => {
209+
command.arg("--manifest-path");
210+
command.arg(<_ as AsRef<Path>>::as_ref(
211+
&self.workspace_root.join("Cargo.toml"),
212+
));
213+
}
214+
InvocationStrategy::PerWorkspace => {
215+
command.current_dir(&self.workspace_root);
216+
}
217+
}
189218
tracing::debug!(?command, "will restart flycheck");
190219
match CargoHandle::spawn(command) {
191220
Ok(cargo_handle) => {
@@ -194,10 +223,10 @@ impl FlycheckActor {
194223
"did restart flycheck"
195224
);
196225
self.cargo_handle = Some(cargo_handle);
197-
self.progress(Progress::DidStart);
226+
self.report_progress(Progress::DidStart);
198227
}
199228
Err(error) => {
200-
self.progress(Progress::DidFailToRestart(format!(
229+
self.report_progress(Progress::DidFailToRestart(format!(
201230
"Failed to run the following command: {:?} error={}",
202231
self.check_command(),
203232
error
@@ -217,11 +246,11 @@ impl FlycheckActor {
217246
self.check_command()
218247
);
219248
}
220-
self.progress(Progress::DidFinish(res));
249+
self.report_progress(Progress::DidFinish(res));
221250
}
222251
Event::CheckEvent(Some(message)) => match message {
223252
CargoMessage::CompilerArtifact(msg) => {
224-
self.progress(Progress::DidCheckCrate(msg.target.name));
253+
self.report_progress(Progress::DidCheckCrate(msg.target.name));
225254
}
226255

227256
CargoMessage::Diagnostic(msg) => {
@@ -245,7 +274,14 @@ impl FlycheckActor {
245274
"did cancel flycheck"
246275
);
247276
cargo_handle.cancel();
248-
self.progress(Progress::DidCancel);
277+
self.report_progress(Progress::DidCancel);
278+
}
279+
}
280+
281+
fn invocation_strategy(&self) -> InvocationStrategy {
282+
match self.config {
283+
FlycheckConfig::CargoCommand { invocation_strategy, .. }
284+
| FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
249285
}
250286
}
251287

@@ -260,6 +296,7 @@ impl FlycheckActor {
260296
extra_args,
261297
features,
262298
extra_env,
299+
invocation_strategy: _,
263300
} => {
264301
let mut cmd = Command::new(toolchain::cargo());
265302
cmd.arg(command);
@@ -288,7 +325,7 @@ impl FlycheckActor {
288325
cmd.envs(extra_env);
289326
cmd
290327
}
291-
FlycheckConfig::CustomCommand { command, args, extra_env } => {
328+
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => {
292329
let mut cmd = Command::new(command);
293330
cmd.args(args);
294331
cmd.envs(extra_env);

crates/rust-analyzer/src/config.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ config_data! {
123123
///
124124
/// Set to `"all"` to pass `--all-features` to Cargo.
125125
checkOnSave_features: Option<CargoFeatures> = "null",
126+
/// Specifies the invocation strategy to use when running the checkOnSave command.
127+
/// If `per_workspace_with_manifest_path` is set, the command will be executed for each
128+
/// workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
129+
/// the command will be executed from the project root.
130+
/// If `per_workspace` is set, the command will be executed for each workspace and the
131+
/// command will be executed from the corresponding workspace root.
132+
/// If `once_in_root` is set, the command will be executed once in the project root.
133+
checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
126134
/// Whether to pass `--no-default-features` to Cargo. Defaults to
127135
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
128136
checkOnSave_noDefaultFeatures: Option<bool> = "null",
@@ -1077,6 +1085,13 @@ impl Config {
10771085
if !self.data.checkOnSave_enable {
10781086
return None;
10791087
}
1088+
let invocation_strategy = match self.data.cargo_buildScripts_invocationStrategy {
1089+
InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot,
1090+
InvocationStrategy::PerWorkspaceWithManifestPath => {
1091+
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
1092+
}
1093+
InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace,
1094+
};
10801095
let flycheck_config = match &self.data.checkOnSave_overrideCommand {
10811096
Some(args) if !args.is_empty() => {
10821097
let mut args = args.clone();
@@ -1085,6 +1100,7 @@ impl Config {
10851100
command,
10861101
args,
10871102
extra_env: self.check_on_save_extra_env(),
1103+
invocation_strategy,
10881104
}
10891105
}
10901106
Some(_) | None => FlycheckConfig::CargoCommand {
@@ -1114,6 +1130,7 @@ impl Config {
11141130
},
11151131
extra_args: self.data.checkOnSave_extraArgs.clone(),
11161132
extra_env: self.check_on_save_extra_env(),
1133+
invocation_strategy,
11171134
},
11181135
};
11191136
Some(flycheck_config)

crates/rust-analyzer/src/reload.rs

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

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

docs/user/generated_config.adoc

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

124124
Set to `"all"` to pass `--all-features` to Cargo.
125125
--
126+
[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`)::
127+
+
128+
--
129+
Specifies the invocation strategy to use when running the checkOnSave command.
130+
If `per_workspace_with_manifest_path` is set, the command will be executed for each
131+
workspace, `--manifest-path {workspace-dir}` will be passed to the invoked command and
132+
the command will be executed from the project root.
133+
If `per_workspace` is set, the command will be executed for each workspace and the
134+
command will be executed from the corresponding workspace root.
135+
If `once_in_root` is set, the command will be executed once in the project root.
136+
--
126137
[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
127138
+
128139
--

editors/code/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,21 @@
558558
}
559559
]
560560
},
561+
"rust-analyzer.checkOnSave.invocationStrategy": {
562+
"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.",
563+
"default": "per_workspace",
564+
"type": "string",
565+
"enum": [
566+
"per_workspace",
567+
"per_workspace_with_manifest_path",
568+
"once_in_root"
569+
],
570+
"enumDescriptions": [
571+
"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.",
572+
"The command will be executed for each workspace and the command will be executed from the corresponding workspace root.",
573+
"The command will be executed once in the project root."
574+
]
575+
},
561576
"rust-analyzer.checkOnSave.noDefaultFeatures": {
562577
"markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
563578
"default": null,

0 commit comments

Comments
 (0)