mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 06:41:48 +00:00
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:
parent
7e2c41dbd6
commit
4a287d2525
5 changed files with 127 additions and 34 deletions
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt, io,
|
fmt, io,
|
||||||
|
path::Path,
|
||||||
process::{ChildStderr, ChildStdout, Command, Stdio},
|
process::{ChildStderr, ChildStdout, Command, Stdio},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +22,14 @@ pub use cargo_metadata::diagnostic::{
|
||||||
DiagnosticSpanMacroExpansion,
|
DiagnosticSpanMacroExpansion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum InvocationStrategy {
|
||||||
|
OnceInRoot,
|
||||||
|
PerWorkspaceWithManifestPath,
|
||||||
|
#[default]
|
||||||
|
PerWorkspace,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum FlycheckConfig {
|
pub enum FlycheckConfig {
|
||||||
CargoCommand {
|
CargoCommand {
|
||||||
|
@ -32,11 +41,13 @@ pub enum FlycheckConfig {
|
||||||
features: Vec<String>,
|
features: Vec<String>,
|
||||||
extra_args: Vec<String>,
|
extra_args: Vec<String>,
|
||||||
extra_env: FxHashMap<String, String>,
|
extra_env: FxHashMap<String, String>,
|
||||||
|
invocation_strategy: InvocationStrategy,
|
||||||
},
|
},
|
||||||
CustomCommand {
|
CustomCommand {
|
||||||
command: String,
|
command: String,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
extra_env: FxHashMap<String, String>,
|
extra_env: FxHashMap<String, String>,
|
||||||
|
invocation_strategy: InvocationStrategy,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +147,9 @@ enum Restart {
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`FlycheckActor`] is a single check instance of a workspace.
|
||||||
struct FlycheckActor {
|
struct FlycheckActor {
|
||||||
|
/// The workspace id of this flycheck instance.
|
||||||
id: usize,
|
id: usize,
|
||||||
sender: Box<dyn Fn(Message) + Send>,
|
sender: Box<dyn Fn(Message) + Send>,
|
||||||
config: FlycheckConfig,
|
config: FlycheckConfig,
|
||||||
|
@ -164,9 +177,11 @@ impl FlycheckActor {
|
||||||
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
|
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
|
||||||
FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
|
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 });
|
self.send(Message::Progress { id: self.id, progress });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
|
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
|
||||||
let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
|
let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
|
||||||
if let Ok(msg) = inbox.try_recv() {
|
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())),
|
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self, inbox: Receiver<Restart>) {
|
fn run(mut self, inbox: Receiver<Restart>) {
|
||||||
'event: while let Some(event) = self.next_event(&inbox) {
|
'event: while let Some(event) = self.next_event(&inbox) {
|
||||||
match event {
|
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");
|
tracing::debug!(?command, "will restart flycheck");
|
||||||
match CargoHandle::spawn(command) {
|
match CargoHandle::spawn(command) {
|
||||||
Ok(cargo_handle) => {
|
Ok(cargo_handle) => {
|
||||||
|
@ -203,10 +232,10 @@ impl FlycheckActor {
|
||||||
"did restart flycheck"
|
"did restart flycheck"
|
||||||
);
|
);
|
||||||
self.cargo_handle = Some(cargo_handle);
|
self.cargo_handle = Some(cargo_handle);
|
||||||
self.progress(Progress::DidStart);
|
self.report_progress(Progress::DidStart);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
self.progress(Progress::DidFailToRestart(format!(
|
self.report_progress(Progress::DidFailToRestart(format!(
|
||||||
"Failed to run the following command: {:?} error={}",
|
"Failed to run the following command: {:?} error={}",
|
||||||
self.check_command(),
|
self.check_command(),
|
||||||
error
|
error
|
||||||
|
@ -226,11 +255,11 @@ impl FlycheckActor {
|
||||||
self.check_command()
|
self.check_command()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.progress(Progress::DidFinish(res));
|
self.report_progress(Progress::DidFinish(res));
|
||||||
}
|
}
|
||||||
Event::CheckEvent(Some(message)) => match message {
|
Event::CheckEvent(Some(message)) => match message {
|
||||||
CargoMessage::CompilerArtifact(msg) => {
|
CargoMessage::CompilerArtifact(msg) => {
|
||||||
self.progress(Progress::DidCheckCrate(msg.target.name));
|
self.report_progress(Progress::DidCheckCrate(msg.target.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
CargoMessage::Diagnostic(msg) => {
|
CargoMessage::Diagnostic(msg) => {
|
||||||
|
@ -254,7 +283,14 @@ impl FlycheckActor {
|
||||||
"did cancel flycheck"
|
"did cancel flycheck"
|
||||||
);
|
);
|
||||||
cargo_handle.cancel();
|
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,
|
extra_args,
|
||||||
features,
|
features,
|
||||||
extra_env,
|
extra_env,
|
||||||
|
invocation_strategy: _,
|
||||||
} => {
|
} => {
|
||||||
let mut cmd = Command::new(toolchain::cargo());
|
let mut cmd = Command::new(toolchain::cargo());
|
||||||
cmd.arg(command);
|
cmd.arg(command);
|
||||||
|
@ -297,7 +334,7 @@ impl FlycheckActor {
|
||||||
cmd.envs(extra_env);
|
cmd.envs(extra_env);
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
FlycheckConfig::CustomCommand { command, args, extra_env } => {
|
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy: _ } => {
|
||||||
let mut cmd = Command::new(command);
|
let mut cmd = Command::new(command);
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
cmd.envs(extra_env);
|
cmd.envs(extra_env);
|
||||||
|
|
|
@ -130,6 +130,14 @@ config_data! {
|
||||||
///
|
///
|
||||||
/// Set to `"all"` to pass `--all-features` to Cargo.
|
/// Set to `"all"` to pass `--all-features` to Cargo.
|
||||||
checkOnSave_features: Option<CargoFeaturesDef> = "null",
|
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
|
/// Whether to pass `--no-default-features` to Cargo. Defaults to
|
||||||
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
|
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
|
||||||
checkOnSave_noDefaultFeatures: Option<bool> = "null",
|
checkOnSave_noDefaultFeatures: Option<bool> = "null",
|
||||||
|
@ -1094,6 +1102,13 @@ impl Config {
|
||||||
if !self.data.checkOnSave_enable {
|
if !self.data.checkOnSave_enable {
|
||||||
return None;
|
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 {
|
let flycheck_config = match &self.data.checkOnSave_overrideCommand {
|
||||||
Some(args) if !args.is_empty() => {
|
Some(args) if !args.is_empty() => {
|
||||||
let mut args = args.clone();
|
let mut args = args.clone();
|
||||||
|
@ -1102,6 +1117,7 @@ impl Config {
|
||||||
command,
|
command,
|
||||||
args,
|
args,
|
||||||
extra_env: self.check_on_save_extra_env(),
|
extra_env: self.check_on_save_extra_env(),
|
||||||
|
invocation_strategy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(_) | None => FlycheckConfig::CargoCommand {
|
Some(_) | None => FlycheckConfig::CargoCommand {
|
||||||
|
@ -1131,6 +1147,7 @@ impl Config {
|
||||||
},
|
},
|
||||||
extra_args: self.data.checkOnSave_extraArgs.clone(),
|
extra_args: self.data.checkOnSave_extraArgs.clone(),
|
||||||
extra_env: self.check_on_save_extra_env(),
|
extra_env: self.check_on_save_extra_env(),
|
||||||
|
invocation_strategy,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Some(flycheck_config)
|
Some(flycheck_config)
|
||||||
|
|
|
@ -473,8 +473,19 @@ impl GlobalState {
|
||||||
};
|
};
|
||||||
|
|
||||||
let sender = self.flycheck_sender.clone();
|
let sender = self.flycheck_sender.clone();
|
||||||
self.flycheck = self
|
let (FlycheckConfig::CargoCommand { invocation_strategy, .. }
|
||||||
.workspaces
|
| 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()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(id, w)| match w {
|
.filter_map(|(id, w)| match w {
|
||||||
|
@ -498,7 +509,9 @@ impl GlobalState {
|
||||||
root.to_path_buf(),
|
root.to_path_buf(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,17 @@ List of features to activate. Defaults to
|
||||||
|
|
||||||
Set to `"all"` to pass `--all-features` to Cargo.
|
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`)::
|
[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
@ -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": {
|
"rust-analyzer.checkOnSave.noDefaultFeatures": {
|
||||||
"markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
|
"markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.",
|
||||||
"default": null,
|
"default": null,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue