Unnamed repository; edit this file 'description' to name the repository.
Implement invocation strategy config for checkOnSave
Note that due to how cargo works, none of the modes currently work for r-a
Lukas Wirth 2022-10-20
parent 7e2c41d · commit 4a287d2
-rw-r--r--crates/flycheck/src/lib.rs53
-rw-r--r--crates/rust-analyzer/src/config.rs17
-rw-r--r--crates/rust-analyzer/src/reload.rs65
-rw-r--r--docs/user/generated_config.adoc11
-rw-r--r--editors/code/package.json15
5 files changed, 127 insertions, 34 deletions
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index e8c63d410a..c3976e6b7a 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -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);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 79f6ded489..a61e38706e 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -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)
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index bd5741f615..5382790f6e 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -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()
+ }
+ };
}
}
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index a5307b6315..3ced42ef72 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -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`)::
+
--
diff --git a/editors/code/package.json b/editors/code/package.json
index b1b565106a..3af32685fd 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -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,