Skip to content

Commit

Permalink
flycheck: initial implementation of $saved_file
Browse files Browse the repository at this point in the history
If the custom command has a $saved_file placeholder, and we know the
file being saved, replace the placeholder and run a check command.

If there's a placeholder and we don't know the saved file, do nothing.
  • Loading branch information
Wilfred committed Oct 4, 2023
1 parent ad73597 commit b5eafbf
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 17 deletions.
50 changes: 40 additions & 10 deletions crates/flycheck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::{

use command_group::{CommandGroup, GroupChild};
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use paths::AbsPathBuf;
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::Deserialize;
use stdx::process::streaming_output;
Expand Down Expand Up @@ -100,8 +100,8 @@ impl FlycheckHandle {
}

/// Schedule a re-start of the cargo check worker.
pub fn restart(&self) {
self.sender.send(StateChange::Restart).unwrap();
pub fn restart(&self, saved_file: Option<AbsPathBuf>) {
self.sender.send(StateChange::Restart { saved_file }).unwrap();
}

/// Stop this cargo check worker.
Expand Down Expand Up @@ -152,7 +152,7 @@ pub enum Progress {
}

enum StateChange {
Restart,
Restart { saved_file: Option<AbsPathBuf> },
Cancel,
}

Expand All @@ -178,6 +178,8 @@ enum Event {
CheckEvent(Option<CargoMessage>),
}

const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";

impl FlycheckActor {
fn new(
id: usize,
Expand Down Expand Up @@ -212,7 +214,7 @@ impl FlycheckActor {
tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
self.cancel_check_process();
}
Event::RequestStateChange(StateChange::Restart) => {
Event::RequestStateChange(StateChange::Restart { saved_file }) => {
// Cancel the previously spawned process
self.cancel_check_process();
while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
Expand All @@ -222,7 +224,10 @@ impl FlycheckActor {
}
}

let command = self.check_command();
let command = match self.check_command(saved_file.as_deref()) {
Some(c) => c,
None => continue,
};
let formatted_command = format!("{:?}", command);

tracing::debug!(?command, "will restart flycheck");
Expand Down Expand Up @@ -296,7 +301,10 @@ impl FlycheckActor {
}
}

fn check_command(&self) -> Command {
/// Construct a `Command` object for checking the user's code. If the user
/// has specified a custom command with placeholders that we cannot fill,
/// return None.
fn check_command(&self, saved_file: Option<&AbsPath>) -> Option<Command> {
let (mut cmd, args) = match &self.config {
FlycheckConfig::CargoCommand {
command,
Expand Down Expand Up @@ -341,7 +349,7 @@ impl FlycheckActor {
}
}
cmd.envs(extra_env);
(cmd, extra_args)
(cmd, extra_args.clone())
}
FlycheckConfig::CustomCommand {
command,
Expand Down Expand Up @@ -370,12 +378,34 @@ impl FlycheckActor {
}
}

(cmd, args)
if args.contains(&SAVED_FILE_PLACEHOLDER.to_owned()) {
// If the custom command has a $saved_file placeholder, and
// we're saving a file, replace the placeholder in the arguments.
if let Some(saved_file) = saved_file {
let args = args
.iter()
.map(|arg| {
if arg == SAVED_FILE_PLACEHOLDER {
saved_file.to_string()
} else {
arg.clone()
}
})
.collect();
(cmd, args)
} else {
// The custom command has a $saved_file placeholder,
// but we had an IDE event that wasn't a file save. Do nothing.
return None;
}
} else {
(cmd, args.clone())
}
}
};

cmd.args(args);
cmd
Some(cmd)
}

fn send(&self, check_task: Message) {
Expand Down
4 changes: 4 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ config_data! {
/// by changing `#rust-analyzer.cargo.check.invocationStrategy#` and
/// `#rust-analyzer.cargo.check.invocationLocation#`.
///
/// If `$saved_file` is part of the command, rust-analyzer will pass
/// the absolute path of the saved file to the provided command. This is
/// intended to be used with non-Cargo build systems.
///
/// An example command would be:
///
/// ```bash
Expand Down
10 changes: 6 additions & 4 deletions crates/rust-analyzer/src/handlers/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub(crate) fn handle_did_save_text_document(
} else if state.config.check_on_save() {
// No specific flycheck was triggered, so let's trigger all of them.
for flycheck in state.flycheck.iter() {
flycheck.restart();
flycheck.restart(None);
}
}
Ok(())
Expand Down Expand Up @@ -282,20 +282,22 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
project_model::ProjectWorkspace::DetachedFiles { .. } => false,
});

let saved_file = vfs_path.as_path().map(|p| p.to_owned());

// Find and trigger corresponding flychecks
for flycheck in world.flycheck.iter() {
for (id, _) in workspace_ids.clone() {
if id == flycheck.id() {
updated = true;
flycheck.restart();
flycheck.restart(saved_file.clone());
continue;
}
}
}
// No specific flycheck was triggered, so let's trigger all of them.
if !updated {
for flycheck in world.flycheck.iter() {
flycheck.restart();
flycheck.restart(saved_file.clone());
}
}
Ok(())
Expand Down Expand Up @@ -337,7 +339,7 @@ pub(crate) fn handle_run_flycheck(
}
// No specific flycheck was triggered, so let's trigger all of them.
for flycheck in state.flycheck.iter() {
flycheck.restart();
flycheck.restart(None);
}
Ok(())
}
3 changes: 1 addition & 2 deletions crates/rust-analyzer/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::{

use always_assert::always;
use crossbeam_channel::{select, Receiver};
use flycheck::FlycheckHandle;
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
use lsp_server::{Connection, Notification, Request};
use lsp_types::notification::Notification as _;
Expand Down Expand Up @@ -299,7 +298,7 @@ impl GlobalState {
if became_quiescent {
if self.config.check_on_save() {
// Project has loaded properly, kick off initial flycheck
self.flycheck.iter().for_each(FlycheckHandle::restart);
self.flycheck.iter().for_each(|flycheck| flycheck.restart(None));
}
if self.config.prefill_caches() {
self.prime_caches_queue.request_op("became quiescent".to_string(), ());
Expand Down
4 changes: 4 additions & 0 deletions docs/user/generated_config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ each of them, with the working directory being the workspace root
by changing `#rust-analyzer.cargo.check.invocationStrategy#` and
`#rust-analyzer.cargo.check.invocationLocation#`.

If `$saved_file` is part of the command, rust-analyzer will pass
the absolute path of the saved file to the provided command. This is
intended to be used with non-Cargo build systems.

An example command would be:

```bash
Expand Down
2 changes: 1 addition & 1 deletion editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@
]
},
"rust-analyzer.check.overrideCommand": {
"markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.check.invocationStrategy#` and\n`#rust-analyzer.cargo.check.invocationLocation#`.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
"markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.check.invocationStrategy#` and\n`#rust-analyzer.cargo.check.invocationLocation#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
"default": null,
"type": [
"null",
Expand Down

0 comments on commit b5eafbf

Please sign in to comment.