@@ -4322,9 +4322,46 @@ fn shell_keep_pipe(cx: &mut Context) {
4322 shell(cx, "keep-pipe:".into(), ShellBehavior::Filter);
4322 shell(cx, "keep-pipe:".into(), ShellBehavior::Filter);
4325−fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
4325+fn shell_impl(
4328+ input: Option<&[u8]>,
4329+) -> anyhow::Result<(Tendril, bool)> {
4326 use std::io::Write;
4330 use std::io::Write;
4327 use std::process::{Command, Stdio};
4331 use std::process::{Command, Stdio};
4332+ let mut process = match Command::new(&shell[0])
4335+ .stdin(Stdio::piped())
4336+ .stdout(Stdio::piped())
4337+ .stderr(Stdio::piped())
4340+ Ok(process) => process,
4342+ log::error!("Failed to start shell: {}", e);
4343+ return Err(e.into());
4346+ if let Some(input) = input {
4347+ let mut stdin = process.stdin.take().unwrap();
4348+ stdin.write_all(input)?;
4350+ let output = process.wait_with_output()?;
4352+ if !output.status.success() {
4353+ if !output.stderr.is_empty() {
4354+ log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr));
4356+ bail!("Command failed");
4359+ let tendril = Tendril::try_from_byte_slice(&output.stdout)
4360+ .map_err(|_| anyhow!("Process did not output valid UTF-8"))?;
4361+ Ok((tendril, output.status.success()))
4364+fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
4328 if cx.editor.config.shell.is_empty() {
4365 if cx.editor.config.shell.is_empty() {
4329 cx.editor.set_error("No shell set".to_owned());
4366 cx.editor.set_error("No shell set".to_owned());
@@ -4338,67 +4375,40 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
4338 Some('|'),
4375 Some('|'),
4339 |_input: &str| Vec::new(),
4376 |_input: &str| Vec::new(),
4340 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
4377 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
4378+ let shell = &cx.editor.config.shell;
4341 if event != PromptEvent::Validate {
4379 if event != PromptEvent::Validate {
4344− let shell = &cx.editor.config.shell;
4345 let (view, doc) = current!(cx.editor);
4382 let (view, doc) = current!(cx.editor);
4346 let selection = doc.selection(view.id);
4383 let selection = doc.selection(view.id);
4348 let mut changes = Vec::with_capacity(selection.len());
4385 let mut changes = Vec::with_capacity(selection.len());
4386+ let text = doc.text().slice(..);
4350 for range in selection.ranges() {
4388 for range in selection.ranges() {
4351− let mut process = match Command::new(&shell[0])
4354− .stdin(Stdio::piped())
4355− .stdout(Stdio::piped())
4356− .stderr(Stdio::piped())
4359− Ok(process) => process,
4361− log::error!("Failed to start shell: {}", e);
4362− cx.editor.set_error("Failed to start shell".to_owned());
4367− let stdin = process.stdin.as_mut().unwrap();
4368− let fragment = range.fragment(doc.text().slice(..));
4389+ let fragment = range.fragment(text);
4369− stdin.write_all(fragment.as_bytes()).unwrap();
4390+ let (output, success) =
4370− }
4391+ match shell_impl(shell, input, pipe.then(|| fragment.as_bytes())) {
4371− let output = process.wait_with_output().unwrap();
4392+ Ok(result) => result,
4373− if behavior != ShellBehavior::Filter {
4374− if !output.status.success() {
4375− if !output.stderr.is_empty() {
4376− log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr));
4378− cx.editor.set_error("Command failed".to_owned());
4381− let tendril = match Tendril::try_from_byte_slice(&output.stdout) {
4382− Ok(tendril) => tendril,
4383− Err(_) => {
4393+ Err(err) => {
4384− cx.editor
4394+ cx.editor.set_error(err.to_string());
4385− .set_error("Process did not output valid UTF-8".to_owned());
4399+ if behavior != ShellBehavior::Filter {
4389 let (from, to) = match behavior {
4400 let (from, to) = match behavior {
4390 ShellBehavior::Replace => (range.from(), range.to()),
4401 ShellBehavior::Replace => (range.from(), range.to()),
4391 ShellBehavior::Insert => (range.from(), range.from()),
4402 ShellBehavior::Insert => (range.from(), range.from()),
4392 ShellBehavior::Append => (range.to(), range.to()),
4403 ShellBehavior::Append => (range.to(), range.to()),
4393 _ => (range.from(), range.from()),
4404 _ => (range.from(), range.from()),
4395− changes.push((from, to, Some(tendril)));
4406+ changes.push((from, to, Some(output)));
4396 } else {
4407 } else {
4397 // if the process exits successfully, keep the selection, otherwise delete it.
4408 // if the process exits successfully, keep the selection, otherwise delete it.
4398− let keep = output.status.success();
4399 changes.push((
4409 changes.push((
4400 range.from(),
4410 range.from(),
4401− if keep { range.from() } else { range.to() },
4411+ if success { range.from() } else { range.to() },