From 5b8bfc547607602a557c6115bb5edb557099f8f2 Mon Sep 17 00:00:00 2001
From: Cyrill Schenkel
Date: Tue, 19 Mar 2024 13:30:34 +0100
Subject: Prevent `shell_keep_pipe` from stopping on nonzero exit status code
 (#9817)

The `shell_impl` and `shell_impl_async` functions no longer return
`success` because it was always `true`. If the command didn't succeed
both functions would return an `Err`.

This was also the reason, why `shell_keep_pipe` didn't work. It relied
upon the value of `success` and aborted in case of an `Err`.
It now removes any selection for which `shell_impl` returns `Err`.

If the command always fails, the selections are preserved and an error
message is displayed in the status bar.---
 helix-term/src/commands.rs       | 30 +++++++++---------------------
 helix-term/src/commands/typed.rs |  8 ++------
 2 files changed, 11 insertions(+), 27 deletions(-)

(limited to 'helix-term/src')

diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 133f2d54..a5e79a53 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -5436,16 +5436,9 @@ fn shell_keep_pipe(cx: &mut Context) {
 
             for (i, range) in selection.ranges().iter().enumerate() {
                 let fragment = range.slice(text);
-                let (_output, success) = match shell_impl(shell, input, Some(fragment.into())) {
-                    Ok(result) => result,
-                    Err(err) => {
-                        cx.editor.set_error(err.to_string());
-                        return;
-                    }
-                };
-
-                // if the process exits successfully, keep the selection
-                if success {
+                if let Err(err) = shell_impl(shell, input, Some(fragment.into())) {
+                    log::debug!("Shell command failed: {}", err);
+                } else {
                     ranges.push(*range);
                     if i >= old_index && index.is_none() {
                         index = Some(ranges.len() - 1);
@@ -5464,7 +5457,7 @@ fn shell_keep_pipe(cx: &mut Context) {
     );
 }
 
-fn shell_impl(shell: &[String], cmd: &str, input: Option<Rope>) -> anyhow::Result<(Tendril, bool)> {
+fn shell_impl(shell: &[String], cmd: &str, input: Option<Rope>) -> anyhow::Result<Tendril> {
     tokio::task::block_in_place(|| helix_lsp::block_on(shell_impl_async(shell, cmd, input)))
 }
 
@@ -5472,7 +5465,7 @@ async fn shell_impl_async(
     shell: &[String],
     cmd: &str,
     input: Option<Rope>,
-) -> anyhow::Result<(Tendril, bool)> {
+) -> anyhow::Result<Tendril> {
     use std::process::Stdio;
     use tokio::process::Command;
     ensure!(!shell.is_empty(), "No shell set");
@@ -5535,7 +5528,7 @@ async fn shell_impl_async(
     let str = std::str::from_utf8(&output.stdout)
         .map_err(|_| anyhow!("Process did not output valid UTF-8"))?;
     let tendril = Tendril::from(str);
-    Ok((tendril, output.status.success()))
+    Ok(tendril)
 }
 
 fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
@@ -5556,14 +5549,14 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
     let mut shell_output: Option<Tendril> = None;
     let mut offset = 0isize;
     for range in selection.ranges() {
-        let (output, success) = if let Some(output) = shell_output.as_ref() {
-            (output.clone(), true)
+        let output = if let Some(output) = shell_output.as_ref() {
+            output.clone()
         } else {
             let fragment = range.slice(text);
             match shell_impl(shell, cmd, pipe.then(|| fragment.into())) {
                 Ok(result) => {
                     if !pipe {
-                        shell_output = Some(result.0.clone());
+                        shell_output = Some(result.clone());
                     }
                     result
                 }
@@ -5574,11 +5567,6 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
             }
         };
 
-        if !success {
-            cx.editor.set_error("Command failed");
-            return;
-        }
-
         let output_len = output.chars().count();
 
         let (from, to, deleted_len) = match behavior {
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 2ca3a561..593837e2 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -2261,7 +2261,7 @@ fn run_shell_command(
     let args = args.join(" ");
 
     let callback = async move {
-        let (output, success) = shell_impl_async(&shell, &args, None).await?;
+        let output = shell_impl_async(&shell, &args, None).await?;
         let call: job::Callback = Callback::EditorCompositor(Box::new(
             move |editor: &mut Editor, compositor: &mut Compositor| {
                 if !output.is_empty() {
@@ -2274,11 +2274,7 @@ fn run_shell_command(
                     ));
                     compositor.replace_or_push("shell", popup);
                 }
-                if success {
-                    editor.set_status("Command succeeded");
-                } else {
-                    editor.set_error("Command failed");
-                }
+                editor.set_status("Command succeeded");
             },
         ));
         Ok(call)
-- 
cgit v1.2.3-70-g09d2