@@ -1,4 +1,6 @@
1use anyhow::{anyhow, bail, Context, Error};
1use anyhow::{anyhow, bail, Context, Error};
2+use futures_util::future::BoxFuture;
3+use futures_util::FutureExt;
2use helix_core::auto_pairs::AutoPairs;
4use helix_core::auto_pairs::AutoPairs;
3use helix_core::Range;
5use helix_core::Range;
4use serde::de::{self, Deserialize, Deserializer};
6use serde::de::{self, Deserialize, Deserializer};
@@ -20,7 +22,6 @@ use helix_core::{
20 ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, State, Syntax, Transaction,
22 ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, State, Syntax, Transaction,
21 DEFAULT_LINE_ENDING,
23 DEFAULT_LINE_ENDING,
23−use helix_lsp::util::LspFormatting;
25use crate::{DocumentId, Editor, ViewId};
26use crate::{DocumentId, Editor, ViewId};
@@ -397,7 +398,7 @@ impl Document {
398 /// The same as [`format`], but only returns formatting changes if auto-formatting
399 /// The same as [`format`], but only returns formatting changes if auto-formatting
399 /// is configured.
400 /// is configured.
400− pub fn auto_format(&self) -> Option<impl Future<Output = LspFormatting> + 'static> {
401+ pub fn auto_format(&self) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
401 if self.language_config()?.auto_format {
402 if self.language_config()?.auto_format {
402 self.format()
403 self.format()
@@ -407,7 +408,56 @@ impl Document {
408 /// If supported, returns the changes that should be applied to this document in order
409 /// If supported, returns the changes that should be applied to this document in order
409 /// to format it nicely.
410 /// to format it nicely.
411+ // We can't use anyhow::Result here since the output of the future has to be
412+ // clonable to be used as shared future. So use a custom error type.
410− pub fn format(&self) -> Option<impl Future<Output = LspFormatting> + 'static> {
413+ pub fn format(&self) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
414+ if let Some(formatter) = self.language_config().and_then(|c| c.formatter.clone()) {
415+ use std::process::Stdio;
416+ let text = self.text().clone();
417+ let mut process = tokio::process::Command::new(&formatter.command);
419+ .args(&formatter.args)
420+ .stdin(Stdio::piped())
421+ .stdout(Stdio::piped())
422+ .stderr(Stdio::piped());
424+ let formatting_future = async move {
425+ let mut process = process
427+ .map_err(|e| FormatterError::SpawningFailed {
428+ command: formatter.command.clone(),
432+ let mut stdin = process.stdin.take().ok_or(FormatterError::BrokenStdin)?;
433+ to_writer(&mut stdin, encoding::UTF_8, &text)
435+ .map_err(|_| FormatterError::BrokenStdin)?;
438+ let output = process
441+ .map_err(|_| FormatterError::WaitForOutputFailed)?;
443+ if !output.stderr.is_empty() {
444+ return Err(FormatterError::Stderr(
445+ String::from_utf8_lossy(&output.stderr).to_string(),
449+ if !output.status.success() {
450+ return Err(FormatterError::NonZeroExitStatus);
453+ let str = String::from_utf8(output.stdout)
454+ .map_err(|_| FormatterError::InvalidUtf8Output)?;
456+ Ok(helix_core::diff::compare_ropes(&text, &Rope::from(str)))
458+ return Some(formatting_future.boxed());
411 let language_server = self.language_server()?;
461 let language_server = self.language_server()?;
412 let text = self.text.clone();
462 let text = self.text.clone();
413 let offset_encoding = language_server.offset_encoding();
463 let offset_encoding = language_server.offset_encoding();
@@ -427,13 +477,13 @@ impl Document {
427 log::warn!("LSP formatting failed: {}", e);
477 log::warn!("LSP formatting failed: {}", e);
428 Default::default()
478 Default::default()
430− LspFormatting {
480+ Ok(helix_lsp::util::generate_transaction_from_edits(
431− doc: text,
481+ &text,
433 offset_encoding,
483 offset_encoding,
436− Some(fut)
486+ Some(fut.boxed())
439 pub fn save(&mut self, force: bool) -> impl Future<Output = Result<(), anyhow::Error>> {
489 pub fn save(&mut self, force: bool) -> impl Future<Output = Result<(), anyhow::Error>> {
@@ -442,7 +492,7 @@ impl Document {
443 pub fn format_and_save(
493 pub fn format_and_save(
444 &mut self,
494 &mut self,
445− formatting: Option<impl Future<Output = LspFormatting>>,
495+ formatting: Option<impl Future<Output = Result<Transaction, FormatterError>>>,
446 force: bool,
496 force: bool,
447 ) -> impl Future<Output = anyhow::Result<()>> {
497 ) -> impl Future<Output = anyhow::Result<()>> {
448 self.save_impl(formatting, force)
498 self.save_impl(formatting, force)
@@ -454,7 +504,7 @@ impl Document {
454 /// at its `path()`.
504 /// at its `path()`.
456 /// If `formatting` is present, it supplies some changes that we apply to the text before saving.
506 /// If `formatting` is present, it supplies some changes that we apply to the text before saving.
457− fn save_impl<F: Future<Output = LspFormatting>>(
507+ fn save_impl<F: Future<Output = Result<Transaction, FormatterError>>>(
458 &mut self,
508 &mut self,
459 formatting: Option<F>,
509 formatting: Option<F>,
460 force: bool,
510 force: bool,
@@ -488,7 +538,8 @@ impl Document {
490 if let Some(fmt) = formatting {
540 if let Some(fmt) = formatting {
541+ let transaction = fmt.await?;
491− let success = Transaction::from(fmt.await).changes().apply(&mut text);
542+ let success = transaction.changes().apply(&mut text);
492 if !success {
543 if !success {
493 // This shouldn't happen, because the transaction changes were generated
544 // This shouldn't happen, because the transaction changes were generated
494 // from the same text we're saving.
545 // from the same text we're saving.
@@ -1034,6 +1085,38 @@ impl Default for Document {
1088+#[derive(Clone, Debug)]
1089+pub enum FormatterError {
1092+ error: std::io::ErrorKind,
1095+ WaitForOutputFailed,
1098+ DiskReloadError(String),
1102+impl std::error::Error for FormatterError {}
1104+impl Display for FormatterError {
1105+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1107+ Self::SpawningFailed { command, error } => {
1108+ write!(f, "Failed to spawn formatter {}: {:?}", command, error)
1110+ Self::BrokenStdin => write!(f, "Could not write to formatter stdin"),
1111+ Self::WaitForOutputFailed => write!(f, "Waiting for formatter output failed"),
1112+ Self::Stderr(output) => write!(f, "Formatter error: {}", output),
1113+ Self::InvalidUtf8Output => write!(f, "Invalid UTF-8 formatter output"),
1114+ Self::DiskReloadError(error) => write!(f, "Error reloading file from disk: {}", error),
1115+ Self::NonZeroExitStatus => write!(f, "Formatter exited with non zero exit status:"),
1037#[cfg(test)]
1120#[cfg(test)]
1038mod test {
1121mod test {
1039 use super::*;
1122 use super::*;