Skip to content

Commit

Permalink
Use only stdio type formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
PiergiorgioZagaria committed Jul 8, 2022
1 parent 016e724 commit f7ecf9c
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 103 deletions.
2 changes: 0 additions & 2 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ pub struct FormatterConfiguration {
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub args: Vec<String>,
#[serde(rename = "type")]
pub type_: FormatterType,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
Expand Down
15 changes: 6 additions & 9 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ fn write_impl(
) -> anyhow::Result<()> {
let auto_format = cx.editor.config().auto_format;
let jobs = &mut cx.jobs;
let (view, doc) = current!(cx.editor);
let doc = doc_mut!(cx.editor);

if let Some(ref path) = path {
doc.set_path(Some(path.as_ref().as_ref()))
Expand All @@ -249,7 +249,7 @@ fn write_impl(
bail!("cannot write a buffer without a filename");
}
let fmt = if auto_format {
doc.auto_format(view.id).map(|fmt| {
doc.auto_format().map(|fmt| {
let shared = fmt.shared();
let callback = make_format_callback(
doc.id(),
Expand Down Expand Up @@ -322,8 +322,8 @@ fn format(
return Ok(());
}

let (view, doc) = current!(cx.editor);
if let Some(format) = doc.format(view.id) {
let doc = doc!(cx.editor);
if let Some(format) = doc.format() {
let callback =
make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format);
cx.jobs.callback(callback);
Expand Down Expand Up @@ -552,10 +552,7 @@ fn write_all_impl(
let jobs = &mut cx.jobs;
// save all documents
// Saves only visible buffers? How do we reload the not visible ones?
for (view, _) in cx.editor.tree.views() {
let id = view.doc;
let doc = cx.editor.documents.get_mut(&id).unwrap();

for doc in &mut cx.editor.documents.values_mut() {
if doc.path().is_none() {
errors.push_str("cannot write a buffer without a filename\n");
continue;
Expand All @@ -566,7 +563,7 @@ fn write_all_impl(
}

let fmt = if auto_format {
doc.auto_format(view.id).map(|fmt| {
doc.auto_format().map(|fmt| {
let shared = fmt.shared();
let callback = make_format_callback(
doc.id(),
Expand Down
133 changes: 41 additions & 92 deletions helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use anyhow::{anyhow, bail, Context, Error};
use futures_util::future::BoxFuture;
use futures_util::FutureExt;
use helix_core::auto_pairs::AutoPairs;
use helix_core::syntax::FormatterType;
use helix_core::Range;
use serde::de::{self, Deserialize, Deserializer};
use serde::Serialize;
Expand Down Expand Up @@ -400,12 +399,9 @@ impl Document {

/// The same as [`format`], but only returns formatting changes if auto-formatting
/// is configured.
pub fn auto_format(
&mut self,
view_id: ViewId,
) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
pub fn auto_format(&self) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
if self.language_config()?.auto_format {
self.format(view_id)
self.format()
} else {
None
}
Expand All @@ -415,97 +411,50 @@ impl Document {
/// to format it nicely.
// We can't use anyhow::Result here since the output of the future has to be
// clonable to be used as shared future. So use a custom error type.
pub fn format(
&mut self,
view_id: ViewId,
) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
pub fn format(&self) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
if let Some(formatter) = self.language_config().and_then(|c| c.formatter.clone()) {
use std::process::Stdio;
match formatter.type_ {
FormatterType::Stdio => {
let text = self.text().clone();
let mut process = tokio::process::Command::new(&formatter.command);
process
.args(&formatter.args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());

let formatting_future = async move {
let mut process =
process
.spawn()
.map_err(|e| FormatterError::SpawningFailed {
command: formatter.command.clone(),
error: e.kind(),
})?;
{
let mut stdin =
process.stdin.take().ok_or(FormatterError::BrokenStdin)?;
stdin
.write_all(&text.bytes().collect::<Vec<u8>>())
.await
.map_err(|_| FormatterError::BrokenStdin)?;
}

let output = process
.wait_with_output()
.await
.map_err(|_| FormatterError::WaitForOutputFailed)?;

if !output.stderr.is_empty() {
return Err(FormatterError::Stderr(
String::from_utf8_lossy(&output.stderr).to_string(),
));
}

let str = String::from_utf8(output.stdout)
.map_err(|_| FormatterError::InvalidUtf8Output)?;

Ok(helix_core::diff::compare_ropes(&text, &Rope::from(str)))
};
return Some(formatting_future.boxed());
let text = self.text().clone();
let mut process = tokio::process::Command::new(&formatter.command);
process
.args(&formatter.args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());

let formatting_future = async move {
let mut process = process
.spawn()
.map_err(|e| FormatterError::SpawningFailed {
command: formatter.command.clone(),
error: e.kind(),
})?;
{
let mut stdin = process.stdin.take().ok_or(FormatterError::BrokenStdin)?;
stdin
.write_all(&text.bytes().collect::<Vec<u8>>())
.await
.map_err(|_| FormatterError::BrokenStdin)?;
}
FormatterType::File => {
let path = self.path()?.clone();
let process = std::process::Command::new(&formatter.command)
.args(&formatter.args)
.arg(path.to_str().unwrap_or(""))
.stderr(Stdio::piped())
.spawn();

// File type formatters are run synchronously since the doc
// has to be reloaded from disk rather than being resolved
// through a transaction.
let format_and_reload = || -> Result<(), FormatterError> {
let process = process.map_err(|e| FormatterError::SpawningFailed {
command: formatter.command.clone(),
error: e.kind(),
})?;
let output = process
.wait_with_output()
.map_err(|_| FormatterError::WaitForOutputFailed)?;

if !output.stderr.is_empty() {
return Err(FormatterError::Stderr(
String::from_utf8_lossy(&output.stderr).to_string(),
));
} else if let Err(e) = self.reload(view_id) {
return Err(FormatterError::DiskReloadError(e.to_string()));
}

Ok(())
};

let format_result = format_and_reload();
let text = self.text().clone();

// Generate an empty transaction as a placeholder
let future = async move { format_result.map(|_| Transaction::new(&text)) };
return Some(future.boxed());

let output = process
.wait_with_output()
.await
.map_err(|_| FormatterError::WaitForOutputFailed)?;

if !output.stderr.is_empty() {
return Err(FormatterError::Stderr(
String::from_utf8_lossy(&output.stderr).to_string(),
));
}

let str = String::from_utf8(output.stdout)
.map_err(|_| FormatterError::InvalidUtf8Output)?;

Ok(helix_core::diff::compare_ropes(&text, &Rope::from(str)))
};
}
return Some(formatting_future.boxed());
};

let language_server = self.language_server()?;
let text = self.text.clone();
Expand Down
2 changes: 2 additions & 0 deletions languages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@ auto-format = true
comment-token = "//"
language-server = { command = "zls" }
indent = { tab-width = 4, unit = " " }
formatter = { command = "zig" , args = ["fmt", "--stdin"] }


[[grammar]]
name = "zig"
Expand Down

0 comments on commit f7ecf9c

Please sign in to comment.