Implement invocation strategy config for checkOnSave

Note that due to how cargo works, none of the modes currently work for r-a
This commit is contained in:
Lukas Wirth 2022-09-15 13:28:09 +02:00
parent 7e2c41dbd6
commit 4a287d2525
5 changed files with 127 additions and 34 deletions

View File

@ -6,6 +6,7 @@
use std::{
fmt, io,
path::Path,
process::{ChildStderr, ChildStdout, Command, Stdio},
time::Duration,
};
@ -21,6 +22,14 @@ pub use cargo_metadata::diagnostic::{
DiagnosticSpanMacroExpansion,
};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy {
OnceInRoot,
PerWorkspaceWithManifestPath,
#[default]
PerWorkspace,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig {
CargoCommand {
@ -32,11 +41,13 @@ pub enum FlycheckConfig {
features: Vec<String>,
extra_args: Vec<String>,
extra_env: FxHashMap<String, String>,
invocation_strategy: InvocationStrategy,
},
CustomCommand {
command: String,
args: Vec<String>,
extra_env: FxHashMap<String, String>,
invocation_strategy: InvocationStrategy,
},
}
@ -136,7 +147,9 @@ enum Restart {
No,
}
/// A [`FlycheckActor`] is a single check instance of a workspace.
struct FlycheckActor {
/// The workspace id of this flycheck instance.
id: usize,
sender: Box<dyn Fn(Message) + Send>,
config: FlycheckConfig,
@ -164,9 +177,11 @@ impl FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
}
fn progress(&self, progress: Progress) {
fn report_progress(&self, progress: Progress) {
self.send(Message::Progress { id: self.id, progress });
}
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
if let Ok(msg) = inbox.try_recv() {
@ -178,6 +193,7 @@ impl FlycheckActor {
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
}
}
fn run(mut self, inbox: Receiver<Restart>) {
'event: while let Some(event) = self.next_event(&inbox) {
match event {
@ -194,7 +210,20 @@ impl FlycheckActor {
}
}
let command = self.check_command();
let mut command = self.check_command();
let invocation_strategy = self.invocation_strategy();
match invocation_strategy {
InvocationStrategy::OnceInRoot => (),
InvocationStrategy::PerWorkspaceWithManifestPath => {
command.arg("--manifest-path");
command.arg(<_ as AsRef<Path>>::as_ref(
&self.workspace_root.join("Cargo.toml"),
));
}
InvocationStrategy::PerWorkspace => {
command.current_dir(&self.workspace_root);
}
}
tracing::debug!(?command, "will restart flycheck");
match CargoHandle::spawn(command) {
Ok(cargo_handle) => {
@ -203,10 +232,10 @@ impl FlycheckActor {
"did restart flycheck"
);
self.cargo_handle = Some(cargo_handle);
self.progress(Progress::DidStart);
self.report_progress(Progress::DidStart);
}
Err(error) => {
self.progress(Progress::DidFailToRestart(format!(
self.report_progress(Progress::DidFailToRestart(format!(
"Failed to run the following command: {:?} error={}",
self.check_command(),
error
@ -226,11 +255,11 @@ impl FlycheckActor {
self.check_command()
);
}
self.progress(Progress::DidFinish(res));
self.report_progress(Progress::DidFinish(res));
}
Event::CheckEvent(Some(message)) => match message {
CargoMessage::CompilerArtifact(msg) => {
self.progress(Progress::DidCheckCrate(msg.target.name));
self.report_progress(Progress::DidCheckCrate(msg.target.name));
}
CargoMessage::Diagnostic(msg) => {
@ -254,7 +283,14 @@ impl FlycheckActor {
"did cancel flycheck"
);
cargo_handle.cancel();
self.progress(Progress::DidCancel);
self.report_progress(Progress::DidCancel);
}
}
fn invocation_strategy(&self) -> InvocationStrategy {
match self.config {
FlycheckConfig::CargoCommand { invocation_strategy, .. }
| FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
}
}
@ -269,6 +305,7 @@ impl FlycheckActor {
extra_args,
features,
extra_env,
invocation_strategy: _,
} => {
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
@ -297,7 +334,7 @@ impl FlycheckActor {
cmd.envs(extra_env);
cmd
}
FlycheckConfig::CustomCommand { command, args, extra_env } => {
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => {
let mut cmd = Command::new(command);
cmd.args(args);
cmd.envs(extra_env);

View File

@ -130,6 +130,14 @@ config_data! {
///
/// Set to `"all"` to pass `--all-features` to Cargo.
checkOnSave_features: Option<CargoFeaturesDef> = "null",
/// Specifies the invocation strategy to use when running the checkOnSave command.
/// If `per_workspace_with_manifest_path` is set, 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.
/// If `per_workspace` is set, the command will be executed for each workspace and the
/// command will be executed from the corresponding workspace root.
/// If `once_in_root` is set, the command will be executed once in the project root.
checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Whether to pass `--no-default-features` to Cargo. Defaults to
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
checkOnSave_noDefaultFeatures: Option<bool> = "null",
@ -1094,6 +1102,13 @@ impl Config {
if !self.data.checkOnSave_enable {
return None;
}
let invocation_strategy = match self.data.cargo_buildScripts_invocationStrategy {
InvocationStrategy::OnceInRoot => flycheck::InvocationStrategy::OnceInRoot,
InvocationStrategy::PerWorkspaceWithManifestPath => {
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
}
InvocationStrategy::PerWorkspace => flycheck::InvocationStrategy::PerWorkspace,
};
let flycheck_config = match &self.data.checkOnSave_overrideCommand {
Some(args) if !args.is_empty() => {
let mut args = args.clone();
@ -1102,6 +1117,7 @@ impl Config {
command,
args,
extra_env: self.check_on_save_extra_env(),
invocation_strategy,
}
}
Some(_) | None => FlycheckConfig::CargoCommand {
@ -1131,6 +1147,7 @@ impl Config {
},
extra_args: self.data.checkOnSave_extraArgs.clone(),
extra_env: self.check_on_save_extra_env(),
invocation_strategy,
},
};
Some(flycheck_config)

View File

@ -473,32 +473,45 @@ impl GlobalState {
};
let sender = self.flycheck_sender.clone();
self.flycheck = self
.workspaces
.iter()
.enumerate()
.filter_map(|(id, w)| match w {
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
ProjectWorkspace::Json { project, .. } => {
// Enable flychecks for json projects if a custom flycheck command was supplied
// in the workspace configuration.
match config {
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
_ => None,
}
}
ProjectWorkspace::DetachedFiles { .. } => None,
})
.map(|(id, root)| {
let sender = sender.clone();
FlycheckHandle::spawn(
id,
Box::new(move |msg| sender.send(msg).unwrap()),
config.clone(),
root.to_path_buf(),
)
})
.collect();
let (FlycheckConfig::CargoCommand { invocation_strategy, .. }
| FlycheckConfig::CustomCommand { invocation_strategy, .. }) = config;
self.flycheck = match invocation_strategy {
flycheck::InvocationStrategy::OnceInRoot => vec![FlycheckHandle::spawn(
0,
Box::new(move |msg| sender.send(msg).unwrap()),
config.clone(),
self.config.root_path().clone(),
)],
flycheck::InvocationStrategy::PerWorkspaceWithManifestPath
| flycheck::InvocationStrategy::PerWorkspace => {
self.workspaces
.iter()
.enumerate()
.filter_map(|(id, w)| match w {
ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
ProjectWorkspace::Json { project, .. } => {
// Enable flychecks for json projects if a custom flycheck command was supplied
// in the workspace configuration.
match config {
FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
_ => None,
}
}
ProjectWorkspace::DetachedFiles { .. } => None,
})
.map(|(id, root)| {
let sender = sender.clone();
FlycheckHandle::spawn(
id,
Box::new(move |msg| sender.send(msg).unwrap()),
config.clone(),
root.to_path_buf(),
)
})
.collect()
}
};
}
}

View File

@ -129,6 +129,17 @@ List of features to activate. Defaults to
Set to `"all"` to pass `--all-features` to Cargo.
--
[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`)::
+
--
Specifies the invocation strategy to use when running the checkOnSave command.
If `per_workspace_with_manifest_path` is set, 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.
If `per_workspace` is set, the command will be executed for each workspace and the
command will be executed from the corresponding workspace root.
If `once_in_root` is set, the command will be executed once in the project root.
--
[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
+
--

View File

@ -561,6 +561,21 @@
}
]
},
"rust-analyzer.checkOnSave.invocationStrategy": {
"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.",
"default": "per_workspace",
"type": "string",
"enum": [
"per_workspace",
"per_workspace_with_manifest_path",
"once_in_root"
],
"enumDescriptions": [
"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.",
"The command will be executed for each workspace and the command will be executed from the corresponding workspace root.",
"The command will be executed once in the project root."
]
},
"rust-analyzer.checkOnSave.noDefaultFeatures": {
"markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
"default": null,