Log prettier errors on failures (#19951)
Closes https://github.com/zed-industries/zed/issues/11987 Release Notes: - Fixed prettier not reporting failures in the status panel on formatting and installation errors
This commit is contained in:
parent
0ba40bdfb8
commit
d49cd0019f
7 changed files with 146 additions and 85 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -16,6 +16,7 @@ dependencies = [
|
||||||
"project",
|
"project",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"ui",
|
"ui",
|
||||||
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ language.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
|
util.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -13,7 +13,8 @@ use language::{
|
||||||
use project::{EnvironmentErrorMessage, LanguageServerProgress, Project, WorktreeId};
|
use project::{EnvironmentErrorMessage, LanguageServerProgress, Project, WorktreeId};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
|
use std::{cmp::Reverse, fmt::Write, sync::Arc, time::Duration};
|
||||||
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle};
|
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
||||||
|
use util::truncate_and_trailoff;
|
||||||
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
||||||
|
|
||||||
actions!(activity_indicator, [ShowErrorMessage]);
|
actions!(activity_indicator, [ShowErrorMessage]);
|
||||||
|
@ -446,6 +447,8 @@ impl ActivityIndicator {
|
||||||
|
|
||||||
impl EventEmitter<Event> for ActivityIndicator {}
|
impl EventEmitter<Event> for ActivityIndicator {}
|
||||||
|
|
||||||
|
const MAX_MESSAGE_LEN: usize = 50;
|
||||||
|
|
||||||
impl Render for ActivityIndicator {
|
impl Render for ActivityIndicator {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let result = h_flex()
|
let result = h_flex()
|
||||||
|
@ -456,6 +459,7 @@ impl Render for ActivityIndicator {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
let this = cx.view().downgrade();
|
let this = cx.view().downgrade();
|
||||||
|
let truncate_content = content.message.len() > MAX_MESSAGE_LEN;
|
||||||
result.gap_2().child(
|
result.gap_2().child(
|
||||||
PopoverMenu::new("activity-indicator-popover")
|
PopoverMenu::new("activity-indicator-popover")
|
||||||
.trigger(
|
.trigger(
|
||||||
|
@ -464,7 +468,21 @@ impl Render for ActivityIndicator {
|
||||||
.id("activity-indicator-status")
|
.id("activity-indicator-status")
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.children(content.icon)
|
.children(content.icon)
|
||||||
.child(Label::new(content.message).size(LabelSize::Small))
|
.map(|button| {
|
||||||
|
if truncate_content {
|
||||||
|
button
|
||||||
|
.child(
|
||||||
|
Label::new(truncate_and_trailoff(
|
||||||
|
&content.message,
|
||||||
|
MAX_MESSAGE_LEN,
|
||||||
|
))
|
||||||
|
.size(LabelSize::Small),
|
||||||
|
)
|
||||||
|
.tooltip(move |cx| Tooltip::text(&content.message, cx))
|
||||||
|
} else {
|
||||||
|
button.child(Label::new(content.message).size(LabelSize::Small))
|
||||||
|
}
|
||||||
|
})
|
||||||
.when_some(content.on_click, |this, handler| {
|
.when_some(content.on_click, |this, handler| {
|
||||||
this.on_click(cx.listener(move |this, _, cx| {
|
this.on_click(cx.listener(move |this, _, cx| {
|
||||||
handler(this, cx);
|
handler(this, cx);
|
||||||
|
|
|
@ -329,11 +329,7 @@ impl Prettier {
|
||||||
})?
|
})?
|
||||||
.context("prettier params calculation")?;
|
.context("prettier params calculation")?;
|
||||||
|
|
||||||
let response = local
|
let response = local.server.request::<Format>(params).await?;
|
||||||
.server
|
|
||||||
.request::<Format>(params)
|
|
||||||
.await
|
|
||||||
.context("prettier format request")?;
|
|
||||||
let diff_task = buffer.update(cx, |buffer, cx| buffer.diff(response.text, cx))?;
|
let diff_task = buffer.update(cx, |buffer, cx| buffer.diff(response.text, cx))?;
|
||||||
Ok(diff_task.await)
|
Ok(diff_task.await)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ use gpui::{
|
||||||
Task, WeakModel,
|
Task, WeakModel,
|
||||||
};
|
};
|
||||||
use http_client::HttpClient;
|
use http_client::HttpClient;
|
||||||
|
use itertools::Itertools as _;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{
|
language_settings::{
|
||||||
language_settings, FormatOnSave, Formatter, LanguageSettings, SelectedFormatter,
|
language_settings, FormatOnSave, Formatter, LanguageSettings, SelectedFormatter,
|
||||||
|
@ -144,7 +145,6 @@ pub struct LocalLspStore {
|
||||||
HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>,
|
HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>,
|
||||||
prettier_store: Model<PrettierStore>,
|
prettier_store: Model<PrettierStore>,
|
||||||
current_lsp_settings: HashMap<LanguageServerName, LspSettings>,
|
current_lsp_settings: HashMap<LanguageServerName, LspSettings>,
|
||||||
last_formatting_failure: Option<String>,
|
|
||||||
_subscription: gpui::Subscription,
|
_subscription: gpui::Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,9 +563,7 @@ impl LocalLspStore {
|
||||||
})?;
|
})?;
|
||||||
prettier_store::format_with_prettier(&prettier, &buffer.handle, cx)
|
prettier_store::format_with_prettier(&prettier, &buffer.handle, cx)
|
||||||
.await
|
.await
|
||||||
.transpose()
|
.transpose()?
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
Formatter::External { command, arguments } => {
|
Formatter::External { command, arguments } => {
|
||||||
Self::format_via_external_command(buffer, command, arguments.as_deref(), cx)
|
Self::format_via_external_command(buffer, command, arguments.as_deref(), cx)
|
||||||
|
@ -705,6 +703,7 @@ impl LspStoreMode {
|
||||||
|
|
||||||
pub struct LspStore {
|
pub struct LspStore {
|
||||||
mode: LspStoreMode,
|
mode: LspStoreMode,
|
||||||
|
last_formatting_failure: Option<String>,
|
||||||
downstream_client: Option<(AnyProtoClient, u64)>,
|
downstream_client: Option<(AnyProtoClient, u64)>,
|
||||||
nonce: u128,
|
nonce: u128,
|
||||||
buffer_store: Model<BufferStore>,
|
buffer_store: Model<BufferStore>,
|
||||||
|
@ -907,7 +906,6 @@ impl LspStore {
|
||||||
language_server_watcher_registrations: Default::default(),
|
language_server_watcher_registrations: Default::default(),
|
||||||
current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
|
current_lsp_settings: ProjectSettings::get_global(cx).lsp.clone(),
|
||||||
buffers_being_formatted: Default::default(),
|
buffers_being_formatted: Default::default(),
|
||||||
last_formatting_failure: None,
|
|
||||||
prettier_store,
|
prettier_store,
|
||||||
environment,
|
environment,
|
||||||
http_client,
|
http_client,
|
||||||
|
@ -917,6 +915,7 @@ impl LspStore {
|
||||||
this.as_local_mut().unwrap().shutdown_language_servers(cx)
|
this.as_local_mut().unwrap().shutdown_language_servers(cx)
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
last_formatting_failure: None,
|
||||||
downstream_client: None,
|
downstream_client: None,
|
||||||
buffer_store,
|
buffer_store,
|
||||||
worktree_store,
|
worktree_store,
|
||||||
|
@ -977,6 +976,7 @@ impl LspStore {
|
||||||
upstream_project_id: project_id,
|
upstream_project_id: project_id,
|
||||||
}),
|
}),
|
||||||
downstream_client: None,
|
downstream_client: None,
|
||||||
|
last_formatting_failure: None,
|
||||||
buffer_store,
|
buffer_store,
|
||||||
worktree_store,
|
worktree_store,
|
||||||
languages: languages.clone(),
|
languages: languages.clone(),
|
||||||
|
@ -5265,9 +5265,9 @@ impl LspStore {
|
||||||
.map(language::proto::serialize_transaction),
|
.map(language::proto::serialize_transaction),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_formatting_failure(&self) -> Option<&str> {
|
pub fn last_formatting_failure(&self) -> Option<&str> {
|
||||||
self.as_local()
|
self.last_formatting_failure.as_deref()
|
||||||
.and_then(|local| local.last_formatting_failure.as_deref())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn environment_for_buffer(
|
pub fn environment_for_buffer(
|
||||||
|
@ -5338,23 +5338,16 @@ impl LspStore {
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
lsp_store.update(&mut cx, |lsp_store, _| {
|
lsp_store.update(&mut cx, |lsp_store, _| {
|
||||||
let local = lsp_store.as_local_mut().unwrap();
|
lsp_store.update_last_formatting_failure(&result);
|
||||||
match &result {
|
|
||||||
Ok(_) => local.last_formatting_failure = None,
|
|
||||||
Err(error) => {
|
|
||||||
local.last_formatting_failure.replace(error.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
} else if let Some((client, project_id)) = self.upstream_client() {
|
} else if let Some((client, project_id)) = self.upstream_client() {
|
||||||
let buffer_store = self.buffer_store();
|
let buffer_store = self.buffer_store();
|
||||||
cx.spawn(move |_, mut cx| async move {
|
cx.spawn(move |lsp_store, mut cx| async move {
|
||||||
let response = client
|
let result = client
|
||||||
.request(proto::FormatBuffers {
|
.request(proto::FormatBuffers {
|
||||||
project_id,
|
project_id,
|
||||||
trigger: trigger as i32,
|
trigger: trigger as i32,
|
||||||
|
@ -5365,13 +5358,21 @@ impl LspStore {
|
||||||
})
|
})
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
})
|
})
|
||||||
.await?
|
.await
|
||||||
.transaction
|
.and_then(|result| result.transaction.context("missing transaction"));
|
||||||
.ok_or_else(|| anyhow!("missing transaction"))?;
|
|
||||||
|
|
||||||
|
lsp_store.update(&mut cx, |lsp_store, _| {
|
||||||
|
lsp_store.update_last_formatting_failure(&result);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let transaction_response = result?;
|
||||||
buffer_store
|
buffer_store
|
||||||
.update(&mut cx, |buffer_store, cx| {
|
.update(&mut cx, |buffer_store, cx| {
|
||||||
buffer_store.deserialize_project_transaction(response, push_to_history, cx)
|
buffer_store.deserialize_project_transaction(
|
||||||
|
transaction_response,
|
||||||
|
push_to_history,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})?
|
})?
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
|
@ -7366,6 +7367,18 @@ impl LspStore {
|
||||||
lsp_action,
|
lsp_action,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_last_formatting_failure<T>(&mut self, formatting_result: &anyhow::Result<T>) {
|
||||||
|
match &formatting_result {
|
||||||
|
Ok(_) => self.last_formatting_failure = None,
|
||||||
|
Err(error) => {
|
||||||
|
let error_string = format!("{error:#}");
|
||||||
|
log::error!("Formatting failed: {error_string}");
|
||||||
|
self.last_formatting_failure
|
||||||
|
.replace(error_string.lines().join(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<LspStoreEvent> for LspStore {}
|
impl EventEmitter<LspStoreEvent> for LspStore {}
|
||||||
|
|
|
@ -104,7 +104,19 @@ impl ErrorExt for anyhow::Error {
|
||||||
if let Some(rpc_error) = self.downcast_ref::<RpcError>() {
|
if let Some(rpc_error) = self.downcast_ref::<RpcError>() {
|
||||||
rpc_error.to_proto()
|
rpc_error.to_proto()
|
||||||
} else {
|
} else {
|
||||||
ErrorCode::Internal.message(format!("{}", self)).to_proto()
|
ErrorCode::Internal
|
||||||
|
.message(
|
||||||
|
format!("{self:#}")
|
||||||
|
.lines()
|
||||||
|
.fold(String::new(), |mut message, line| {
|
||||||
|
if !message.is_empty() {
|
||||||
|
message.push(' ');
|
||||||
|
}
|
||||||
|
message.push_str(line);
|
||||||
|
message
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.to_proto()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2017,8 +2017,7 @@ impl ChannelClient {
|
||||||
mut incoming_rx: mpsc::UnboundedReceiver<Envelope>,
|
mut incoming_rx: mpsc::UnboundedReceiver<Envelope>,
|
||||||
cx: &AsyncAppContext,
|
cx: &AsyncAppContext,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
cx.spawn(|cx| {
|
cx.spawn(|cx| async move {
|
||||||
async move {
|
|
||||||
let peer_id = PeerId { owner_id: 0, id: 0 };
|
let peer_id = PeerId { owner_id: 0, id: 0 };
|
||||||
while let Some(incoming) = incoming_rx.next().await {
|
while let Some(incoming) = incoming_rx.next().await {
|
||||||
let Some(this) = this.upgrade() else {
|
let Some(this) = this.upgrade() else {
|
||||||
|
@ -2030,17 +2029,22 @@ impl ChannelClient {
|
||||||
buffer.pop_front();
|
buffer.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(proto::envelope::Payload::FlushBufferedMessages(_)) =
|
if let Some(proto::envelope::Payload::FlushBufferedMessages(_)) = &incoming.payload
|
||||||
&incoming.payload
|
|
||||||
{
|
{
|
||||||
log::debug!("{}:ssh message received. name:FlushBufferedMessages", this.name);
|
log::debug!(
|
||||||
|
"{}:ssh message received. name:FlushBufferedMessages",
|
||||||
|
this.name
|
||||||
|
);
|
||||||
{
|
{
|
||||||
let buffer = this.buffer.lock();
|
let buffer = this.buffer.lock();
|
||||||
for envelope in buffer.iter() {
|
for envelope in buffer.iter() {
|
||||||
this.outgoing_tx.lock().unbounded_send(envelope.clone()).ok();
|
this.outgoing_tx
|
||||||
|
.lock()
|
||||||
|
.unbounded_send(envelope.clone())
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut envelope = proto::Ack{}.into_envelope(0, Some(incoming.id), None);
|
let mut envelope = proto::Ack {}.into_envelope(0, Some(incoming.id), None);
|
||||||
envelope.id = this.next_message_id.fetch_add(1, SeqCst);
|
envelope.id = this.next_message_id.fetch_add(1, SeqCst);
|
||||||
this.outgoing_tx.lock().unbounded_send(envelope).ok();
|
this.outgoing_tx.lock().unbounded_send(envelope).ok();
|
||||||
continue;
|
continue;
|
||||||
|
@ -2069,25 +2073,41 @@ impl ChannelClient {
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
) {
|
) {
|
||||||
log::debug!("{}:ssh message received. name:{type_name}", this.name);
|
log::debug!("{}:ssh message received. name:{type_name}", this.name);
|
||||||
cx.foreground_executor().spawn(async move {
|
cx.foreground_executor()
|
||||||
|
.spawn(async move {
|
||||||
match future.await {
|
match future.await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
log::debug!("{}:ssh message handled. name:{type_name}", this.name);
|
log::debug!(
|
||||||
|
"{}:ssh message handled. name:{type_name}",
|
||||||
|
this.name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"{}:error handling message. type:{type_name}, error:{error}", this.name,
|
"{}:error handling message. type:{}, error:{}",
|
||||||
|
this.name,
|
||||||
|
type_name,
|
||||||
|
format!("{error:#}").lines().fold(
|
||||||
|
String::new(),
|
||||||
|
|mut message, line| {
|
||||||
|
if !message.is_empty() {
|
||||||
|
message.push(' ');
|
||||||
|
}
|
||||||
|
message.push_str(line);
|
||||||
|
message
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).detach()
|
})
|
||||||
|
.detach()
|
||||||
} else {
|
} else {
|
||||||
log::error!("{}:unhandled ssh message name:{type_name}", this.name);
|
log::error!("{}:unhandled ssh message name:{type_name}", this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue