mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Improve handling of prettier errors on format (#10156)
When no formatter for a language is specified, Zed has the default behaviour: 1. Attempt to format the buffer with `prettier` 2. If that doesn't work, use the language server. The problem was that if `prettier` failed to format a buffer due to legitimate syntax errors, we simply did a fallback to the language server, which would then format over the syntax errors. With JavaScript/React/TypeScript projects this could lead to a situation where 1. Syntax error was introduced 2. Prettier fails 3. Zed ignores the error 4. typescript-language-server formats the buffer despite syntax errors This would lead to some very weird formatting issues. What this PR does is to fix the issue by handling `prettier` errors and results in two user facing changes: 1. When no formatter is set (or set to `auto`) and if we attempted to start a prettier instance to format, we will now display that error and *not* fall back to language server formatting. 2. If the formatter is explicitly set to `prettier`, we will now show errors if we failed to spawn prettier or failed to format with it. This means that we now might show *more* errors than previously, but I think that's better than not showing anything to the user at all. And, of course, it also fixes the issue of invalid syntax being formatted by the language server even though `prettier` failed with an error. Release Notes: - Improved error handling when formatting buffers with `prettier`. Previously `prettier` errors would be logged but ignored. Now `prettier` errors are shown in the UI, just like language server errors when formatting. And if no formatter is specified (or set to `"auto"`) and Zed attempts to use `prettier` for formatting, then `prettier` errors are no longer skipped. That fixes the issue of `prettier` not formatting invalid syntax, but its error being skipped, leading to `typescript-language-server` or another language server formatting invalid syntax.
This commit is contained in:
parent
c0d117182f
commit
8e9543aefe
@ -4,7 +4,7 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context, Result};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::{
|
use futures::{
|
||||||
@ -46,51 +46,48 @@ pub(super) async fn format_with_prettier(
|
|||||||
project: &WeakModel<Project>,
|
project: &WeakModel<Project>,
|
||||||
buffer: &Model<Buffer>,
|
buffer: &Model<Buffer>,
|
||||||
cx: &mut AsyncAppContext,
|
cx: &mut AsyncAppContext,
|
||||||
) -> Option<FormatOperation> {
|
) -> Option<Result<FormatOperation>> {
|
||||||
if let Some((prettier_path, prettier_task)) = project
|
let prettier_instance = project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| {
|
||||||
project.prettier_instance_for_buffer(buffer, cx)
|
project.prettier_instance_for_buffer(buffer, cx)
|
||||||
})
|
})
|
||||||
.ok()?
|
.ok()?
|
||||||
.await
|
.await;
|
||||||
{
|
|
||||||
match prettier_task.await {
|
let Some((prettier_path, prettier_task)) = prettier_instance else {
|
||||||
Ok(prettier) => {
|
return None;
|
||||||
let buffer_path = buffer
|
};
|
||||||
.update(cx, |buffer, cx| {
|
|
||||||
File::from_dyn(buffer.file()).map(|file| file.abs_path(cx))
|
let prettier_description = match prettier_path.as_ref() {
|
||||||
})
|
Some(path) => format!("prettier at {path:?}"),
|
||||||
.ok()?;
|
None => "default prettier instance".to_string(),
|
||||||
match prettier.format(buffer, buffer_path, cx).await {
|
};
|
||||||
Ok(new_diff) => return Some(FormatOperation::Prettier(new_diff)),
|
|
||||||
Err(e) => {
|
match prettier_task.await {
|
||||||
match prettier_path {
|
Ok(prettier) => {
|
||||||
Some(prettier_path) => log::error!(
|
let buffer_path = buffer
|
||||||
"Prettier instance from path {prettier_path:?} failed to format a buffer: {e:#}"
|
.update(cx, |buffer, cx| {
|
||||||
),
|
File::from_dyn(buffer.file()).map(|file| file.abs_path(cx))
|
||||||
None => log::error!(
|
})
|
||||||
"Default prettier instance failed to format a buffer: {e:#}"
|
.ok()?;
|
||||||
),
|
|
||||||
}
|
let format_result = prettier
|
||||||
}
|
.format(buffer, buffer_path, cx)
|
||||||
}
|
.await
|
||||||
}
|
.map(FormatOperation::Prettier)
|
||||||
Err(e) => project
|
.with_context(|| format!("{} failed to format buffer", prettier_description));
|
||||||
|
|
||||||
|
Some(format_result)
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
project
|
||||||
.update(cx, |project, _| {
|
.update(cx, |project, _| {
|
||||||
let instance_to_update = match prettier_path {
|
let instance_to_update = match prettier_path {
|
||||||
Some(prettier_path) => {
|
Some(prettier_path) => project.prettier_instances.get_mut(&prettier_path),
|
||||||
log::error!(
|
None => match &mut project.default_prettier.prettier {
|
||||||
"Prettier instance from path {prettier_path:?} failed to spawn: {e:#}"
|
PrettierInstallation::NotInstalled { .. } => None,
|
||||||
);
|
PrettierInstallation::Installed(instance) => Some(instance),
|
||||||
project.prettier_instances.get_mut(&prettier_path)
|
},
|
||||||
}
|
|
||||||
None => {
|
|
||||||
log::error!("Default prettier instance failed to spawn: {e:#}");
|
|
||||||
match &mut project.default_prettier.prettier {
|
|
||||||
PrettierInstallation::NotInstalled { .. } => None,
|
|
||||||
PrettierInstallation::Installed(instance) => Some(instance),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(instance) = instance_to_update {
|
if let Some(instance) = instance_to_update {
|
||||||
@ -98,11 +95,14 @@ pub(super) async fn format_with_prettier(
|
|||||||
instance.prettier = None;
|
instance.prettier = None;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok()?,
|
.log_err();
|
||||||
|
|
||||||
|
Some(Err(anyhow!(
|
||||||
|
"{} failed to spawn: {error:#}",
|
||||||
|
prettier_description
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DefaultPrettier {
|
pub struct DefaultPrettier {
|
||||||
|
@ -4676,10 +4676,11 @@ impl Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Formatter::Auto, FormatOnSave::On | FormatOnSave::Off) => {
|
(Formatter::Auto, FormatOnSave::On | FormatOnSave::Off) => {
|
||||||
if let Some(new_operation) =
|
let prettier =
|
||||||
prettier_support::format_with_prettier(&project, buffer, &mut cx).await
|
prettier_support::format_with_prettier(&project, buffer, &mut cx).await;
|
||||||
{
|
|
||||||
format_operation = Some(new_operation);
|
if let Some(operation) = prettier {
|
||||||
|
format_operation = Some(operation?);
|
||||||
} else if let Some((language_server, buffer_abs_path)) = server_and_buffer {
|
} else if let Some((language_server, buffer_abs_path)) = server_and_buffer {
|
||||||
format_operation = Some(FormatOperation::Lsp(
|
format_operation = Some(FormatOperation::Lsp(
|
||||||
Self::format_via_lsp(
|
Self::format_via_lsp(
|
||||||
@ -4696,10 +4697,11 @@ impl Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Formatter::Prettier, FormatOnSave::On | FormatOnSave::Off) => {
|
(Formatter::Prettier, FormatOnSave::On | FormatOnSave::Off) => {
|
||||||
if let Some(new_operation) =
|
let prettier =
|
||||||
prettier_support::format_with_prettier(&project, buffer, &mut cx).await
|
prettier_support::format_with_prettier(&project, buffer, &mut cx).await;
|
||||||
{
|
|
||||||
format_operation = Some(new_operation);
|
if let Some(operation) = prettier {
|
||||||
|
format_operation = Some(operation?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user