From 4a76ea8f8834a880d63246c8d43587693ac7cc81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Tue, 31 Aug 2021 18:17:22 +0900 Subject: [PATCH] shell: Move changes outside so we can properly handle errors --- helix-term/src/commands.rs | 123 ++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 6437bf521..42495b7eb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4346,74 +4346,73 @@ fn shell(cx: &mut Context, prompt: &str, behavior: ShellBehavior) { if event == PromptEvent::Validate { let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id); - let mut error: Option<&str> = None; - let transaction = - Transaction::change_by_selection(doc.text(), selection, |range| { - let mut process; - match Command::new(&shell[0]) - .args(&shell[1..]) - .arg(input) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - { - Ok(p) => process = p, - Err(e) => { - log::error!("Failed to start shell: {}", e); - error = Some("Failed to start shell"); - return (0, 0, None); - } - } - if pipe { - let stdin = process.stdin.as_mut().unwrap(); - let fragment = range.fragment(doc.text().slice(..)); - stdin.write_all(fragment.as_bytes()).unwrap(); + + let mut changes = Vec::with_capacity(selection.len()); + + for range in selection.ranges() { + let mut process; + match Command::new(&shell[0]) + .args(&shell[1..]) + .arg(input) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(p) => process = p, + Err(e) => { + log::error!("Failed to start shell: {}", e); + cx.editor.set_error("Failed to start shell".to_owned()); + return; } + } + if pipe { + let stdin = process.stdin.as_mut().unwrap(); + let fragment = range.fragment(doc.text().slice(..)); + stdin.write_all(fragment.as_bytes()).unwrap(); + } - let output = process.wait_with_output().unwrap(); - if behavior != ShellBehavior::Filter { - if !output.status.success() { - let stderr = output.stderr; - if !stderr.is_empty() { - log::error!( - "Shell error: {}", - String::from_utf8_lossy(&stderr) - ); - } - error = Some("Command failed"); - return (0, 0, None); + let output = process.wait_with_output().unwrap(); + if behavior != ShellBehavior::Filter { + if !output.status.success() { + let stderr = output.stderr; + if !stderr.is_empty() { + log::error!("Shell error: {}", String::from_utf8_lossy(&stderr)); } - let stdout = output.stdout; - let tendril; - match Tendril::try_from_byte_slice(&stdout) { - Ok(t) => tendril = t, - Err(_) => { - error = Some("Process did not output valid UTF-8"); - return (0, 0, None); - } + cx.editor.set_error("Command failed".to_owned()); + return; + } + let stdout = output.stdout; + let tendril; + match Tendril::try_from_byte_slice(&stdout) { + Ok(t) => tendril = t, + Err(_) => { + cx.editor + .set_error("Process did not output valid UTF-8".to_owned()); + return; } - let (from, to) = match behavior { - ShellBehavior::Replace => (range.from(), range.to()), - ShellBehavior::Insert => (range.from(), range.from()), - ShellBehavior::Append => (range.to(), range.to()), - _ => (range.from(), range.from()), - }; - (from, to, Some(tendril)) - } else { - // if the process exits successfully, keep the selection, otherwise delete it. - let keep = output.status.success(); - ( - range.from(), - if keep { range.from() } else { range.to() }, - None, - ) } - }); + let (from, to) = match behavior { + ShellBehavior::Replace => (range.from(), range.to()), + ShellBehavior::Insert => (range.from(), range.from()), + ShellBehavior::Append => (range.to(), range.to()), + _ => (range.from(), range.from()), + }; + changes.push((from, to, Some(tendril))); + } else { + // if the process exits successfully, keep the selection, otherwise delete it. + let keep = output.status.success(); + changes.push(( + range.from(), + if keep { range.from() } else { range.to() }, + None, + )); + } + } + + let transaction = Transaction::change(doc.text(), changes.into_iter()); - if let Some(error) = error { - cx.editor.set_error(error.to_owned()); - } else if behavior != ShellBehavior::Ignore { + if behavior != ShellBehavior::Ignore { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); }