Skip to content

Commit

Permalink
Implement invocation strategy config for checkOnSave
Browse files Browse the repository at this point in the history
Note that due to how cargo works, none of the modes currently work for r-a
  • Loading branch information
Veykril committed Sep 15, 2022
1 parent 3af6708 commit c104f0e
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 36 deletions.
55 changes: 46 additions & 9 deletions crates/flycheck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use std::{
fmt, io,
path::Path,
process::{ChildStderr, ChildStdout, Command, Stdio},
time::Duration,
};
Expand All @@ -20,6 +21,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 {
Expand All @@ -30,18 +39,20 @@ pub enum FlycheckConfig {
all_features: bool,
features: Vec<String>,
extra_args: Vec<String>,
invocation_strategy: InvocationStrategy,
},
CustomCommand {
command: String,
args: Vec<String>,
invocation_strategy: InvocationStrategy,
},
}

impl fmt::Display for FlycheckConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
FlycheckConfig::CustomCommand { command, args } => {
FlycheckConfig::CustomCommand { command, args, .. } => {
write!(f, "{} {}", command, args.join(" "))
}
}
Expand Down Expand Up @@ -133,7 +144,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,
Expand Down Expand Up @@ -161,16 +174,19 @@ 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);
select! {
recv(inbox) -> msg => msg.ok().map(Event::Restart),
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
}
}

fn run(mut self, inbox: Receiver<Restart>) {
while let Some(event) = self.next_event(&inbox) {
match event {
Expand All @@ -182,7 +198,20 @@ impl FlycheckActor {
self.cancel_check_process();
while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}

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) => {
Expand All @@ -191,10 +220,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
Expand All @@ -214,11 +243,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) => {
Expand All @@ -242,7 +271,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,
}
}

Expand All @@ -256,6 +292,7 @@ impl FlycheckActor {
all_features,
extra_args,
features,
..
} => {
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
Expand Down Expand Up @@ -283,7 +320,7 @@ impl FlycheckActor {
cmd.args(extra_args);
cmd
}
FlycheckConfig::CustomCommand { command, args } => {
FlycheckConfig::CustomCommand { command, args, .. } => {
let mut cmd = Command::new(command);
cmd.args(args);
cmd
Expand Down
18 changes: 17 additions & 1 deletion crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ config_data! {
///
/// Set to `"all"` to pass `--all-features` to Cargo.
checkOnSave_features: Option<CargoFeatures> = "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",
Expand Down Expand Up @@ -1061,11 +1069,18 @@ 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();
let command = args.remove(0);
FlycheckConfig::CustomCommand { command, args }
FlycheckConfig::CustomCommand { command, args, invocation_strategy }
}
Some(_) | None => FlycheckConfig::CargoCommand {
command: self.data.checkOnSave_command.clone(),
Expand Down Expand Up @@ -1093,6 +1108,7 @@ impl Config {
CargoFeatures::Listed(it) => it,
},
extra_args: self.data.checkOnSave_extraArgs.clone(),
invocation_strategy,
},
};
Some(flycheck_config)
Expand Down
65 changes: 39 additions & 26 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,32 +459,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()
}
};
}
}

Expand Down
11 changes: 11 additions & 0 deletions docs/user/generated_config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,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`)::
+
--
Expand Down
15 changes: 15 additions & 0 deletions editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,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,
Expand Down

0 comments on commit c104f0e

Please sign in to comment.