Unnamed repository; edit this file 'description' to name the repository.
flycheck: Add display_command to pretty-print flycheck command being run in a notification
Cormac Relf 4 months ago
parent 8d8cc93 · commit 5b27dbb
-rw-r--r--crates/rust-analyzer/src/flycheck.rs82
-rw-r--r--crates/toolchain/src/lib.rs3
2 files changed, 85 insertions, 0 deletions
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index 57ad774b18..7f814121e9 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -22,6 +22,7 @@ use serde_derive::Deserialize;
pub(crate) use cargo_metadata::diagnostic::{
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
};
+use toolchain::DISPLAY_COMMAND_IGNORE_ENVS;
use toolchain::Tool;
use triomphe::Arc;
@@ -954,6 +955,54 @@ enum JsonMessage {
Rustc(Diagnostic),
}
+/// Not good enough to execute in a shell, but good enough to show the user without all the noisy
+/// quotes
+///
+/// Pass implicit_cwd if there is one regarded as the obvious by the user, so we can skip showing it.
+/// Compactness is the aim of the game, the output typically gets truncated quite a lot.
+fn display_command(c: &Command, implicit_cwd: Option<&std::path::Path>) -> String {
+ let mut o = String::new();
+ use std::fmt::Write;
+ let lossy = std::ffi::OsStr::to_string_lossy;
+ if let Some(dir) = c.get_current_dir() {
+ if Some(dir) == implicit_cwd.map(std::path::Path::new) {
+ // pass
+ } else if dir.to_string_lossy().contains(" ") {
+ write!(o, "cd {:?} && ", dir).unwrap();
+ } else {
+ write!(o, "cd {} && ", dir.display()).unwrap();
+ }
+ }
+ for (env, val) in c.get_envs() {
+ let (env, val) = (lossy(env), val.map(lossy).unwrap_or(std::borrow::Cow::Borrowed("")));
+ if DISPLAY_COMMAND_IGNORE_ENVS.contains(&env.as_ref()) {
+ continue;
+ }
+ if env.contains(" ") {
+ write!(o, "\"{}={}\" ", env, val).unwrap();
+ } else if val.contains(" ") {
+ write!(o, "{}=\"{}\" ", env, val).unwrap();
+ } else {
+ write!(o, "{}={} ", env, val).unwrap();
+ }
+ }
+ let prog = lossy(c.get_program());
+ if prog.contains(" ") {
+ write!(o, "{:?}", prog).unwrap();
+ } else {
+ write!(o, "{}", prog).unwrap();
+ }
+ for arg in c.get_args() {
+ let arg = lossy(arg);
+ if arg.contains(" ") {
+ write!(o, " \"{}\"", arg).unwrap();
+ } else {
+ write!(o, " {}", arg).unwrap();
+ }
+ }
+ o
+}
+
#[cfg(test)]
mod tests {
use ide_db::FxHashMap;
@@ -962,6 +1011,7 @@ mod tests {
use project_model::project_json;
use crate::flycheck::Substitutions;
+ use crate::flycheck::display_command;
#[test]
fn test_substitutions() {
@@ -1049,4 +1099,36 @@ mod tests {
.map(|args| format!("build {}", args))
}
}
+
+ #[test]
+ fn test_display_command() {
+ use std::path::Path;
+ let workdir = Path::new("workdir");
+ let mut cmd = toolchain::command("command", workdir, &FxHashMap::default());
+ assert_eq!(display_command(cmd.arg("--arg"), Some(workdir)), "command --arg");
+ assert_eq!(
+ display_command(cmd.arg("spaced arg"), Some(workdir)),
+ "command --arg \"spaced arg\""
+ );
+ assert_eq!(
+ display_command(cmd.env("ENVIRON", "yeah"), Some(workdir)),
+ "ENVIRON=yeah command --arg \"spaced arg\""
+ );
+ assert_eq!(
+ display_command(cmd.env("OTHER", "spaced env"), Some(workdir)),
+ "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
+ );
+ assert_eq!(
+ display_command(cmd.current_dir("/tmp"), Some(workdir)),
+ "cd /tmp && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
+ );
+ assert_eq!(
+ display_command(cmd.current_dir("/tmp and/thing"), Some(workdir)),
+ "cd \"/tmp and/thing\" && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
+ );
+ assert_eq!(
+ display_command(cmd.current_dir("/tmp and/thing"), Some(Path::new("/tmp and/thing"))),
+ "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
+ );
+ }
}
diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs
index 39319886cf..1a17269838 100644
--- a/crates/toolchain/src/lib.rs
+++ b/crates/toolchain/src/lib.rs
@@ -74,6 +74,9 @@ impl Tool {
// Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719.
pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0");
+// These get ignored when displaying what command is running in LSP status messages.
+pub const DISPLAY_COMMAND_IGNORE_ENVS: &[&str] = &[NO_RUSTUP_AUTO_INSTALL_ENV.0];
+
#[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */
pub fn command<H>(
cmd: impl AsRef<OsStr>,