From 0325bda89a0b52de465cf01c1d36030fcb071d3b Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 6 Apr 2024 10:17:18 -0600 Subject: [PATCH] Improve lsp notifications (#10220) 1. They now will not go off-screen 2. You can scroll long messages. 3. Only one notification per language server is shown at a time 4. The title/text are now distinguished visually 5. You can copy the error message to the clipboard Fixes: #10217 Fixes: #10190 Fixes: #10090 Release Notes: - Fixed language server notifications being too large ([#10090](https://github.com/zed-industries/zed/issues/10090)). --- crates/workspace/src/notifications.rs | 128 +++++++++++++++++--------- crates/workspace/src/workspace.rs | 2 +- 2 files changed, 83 insertions(+), 47 deletions(-) diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index a4ef2e74d90295b259e33e3e9b07b67392338af3..95768453997a2df8edf6299c31f0c675b1d779c1 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -1,13 +1,14 @@ use crate::{Toast, Workspace}; use collections::HashMap; use gpui::{ - svg, AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, - Global, PromptLevel, Render, Task, View, ViewContext, VisualContext, WindowContext, + svg, AnyView, AppContext, AsyncWindowContext, ClipboardItem, DismissEvent, Entity, EntityId, + EventEmitter, Global, PromptLevel, Render, ScrollHandle, Task, View, ViewContext, + VisualContext, WindowContext, }; use language::DiagnosticSeverity; use std::{any::TypeId, ops::DerefMut}; -use ui::prelude::*; +use ui::{prelude::*, Tooltip}; use util::ResultExt; pub fn init(cx: &mut AppContext) { @@ -100,22 +101,16 @@ impl Workspace { build_notification: impl FnOnce(&mut ViewContext) -> View, ) { let type_id = TypeId::of::(); - if self - .notifications - .iter() - .all(|(existing_type_id, existing_id, _)| { - (*existing_type_id, *existing_id) != (type_id, id) - }) - { - let notification = build_notification(cx); - cx.subscribe(¬ification, move |this, _, _: &DismissEvent, cx| { - this.dismiss_notification_internal(type_id, id, cx); - }) - .detach(); - self.notifications - .push((type_id, id, Box::new(notification))); - cx.notify(); - } + self.dismiss_notification_internal(type_id, id, cx); + + let notification = build_notification(cx); + cx.subscribe(¬ification, move |this, _, _: &DismissEvent, cx| { + this.dismiss_notification_internal(type_id, id, cx); + }) + .detach(); + self.notifications + .push((type_id, id, Box::new(notification))); + cx.notify(); } pub fn show_error(&mut self, err: &E, cx: &mut ViewContext) @@ -174,12 +169,14 @@ impl Workspace { pub struct LanguageServerPrompt { request: Option, + scroll_handle: ScrollHandle, } impl LanguageServerPrompt { pub fn new(request: project::LanguageServerPromptRequest) -> Self { Self { request: Some(request), + scroll_handle: ScrollHandle::new(), } } @@ -211,45 +208,88 @@ impl Render for LanguageServerPrompt { h_flex() .id("language_server_prompt_notification") + .occlude() .elevation_3(cx) .items_start() .justify_between() .p_2() .gap_2() .w_full() + .max_h(vh(0.8, cx)) + .overflow_y_scroll() + .track_scroll(&self.scroll_handle) + .group("") .child( v_flex() + .w_full() .overflow_hidden() .child( h_flex() - .children( - match request.level { - PromptLevel::Info => None, - PromptLevel::Warning => Some(DiagnosticSeverity::WARNING), - PromptLevel::Critical => Some(DiagnosticSeverity::ERROR), - } - .map(|severity| { - svg() - .size(cx.text_style().font_size) - .flex_none() - .mr_1() - .map(|icon| { - if severity == DiagnosticSeverity::ERROR { - icon.path(IconName::ExclamationTriangle.path()) - .text_color(Color::Error.color(cx)) - } else { - icon.path(IconName::ExclamationTriangle.path()) - .text_color(Color::Warning.color(cx)) + .w_full() + .justify_between() + .child( + h_flex() + .flex_grow() + .children( + match request.level { + PromptLevel::Info => None, + PromptLevel::Warning => { + Some(DiagnosticSeverity::WARNING) } - }) - }), + PromptLevel::Critical => { + Some(DiagnosticSeverity::ERROR) + } + } + .map(|severity| { + svg() + .size(cx.text_style().font_size) + .flex_none() + .mr_1() + .mt(px(-2.0)) + .map(|icon| { + if severity == DiagnosticSeverity::ERROR { + icon.path( + IconName::ExclamationTriangle.path(), + ) + .text_color(Color::Error.color(cx)) + } else { + icon.path( + IconName::ExclamationTriangle.path(), + ) + .text_color(Color::Warning.color(cx)) + } + }) + }), + ) + .child( + Label::new(request.lsp_name.clone()) + .size(LabelSize::Default), + ), ) .child( - Label::new(format!("{}:", request.lsp_name)) - .size(LabelSize::Default), + ui::IconButton::new("close", ui::IconName::Close) + .on_click(cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent))), ), ) - .child(Label::new(request.message.to_string())) + .child( + v_flex() + .child( + h_flex().absolute().right_0().rounded_md().child( + ui::IconButton::new("copy", ui::IconName::Copy) + .on_click({ + let message = request.message.clone(); + move |_, cx| { + cx.write_to_clipboard(ClipboardItem::new( + message.clone(), + )) + } + }) + .tooltip(|cx| Tooltip::text("Copy", cx)) + .visible_on_hover(""), + ), + ) + .child(Label::new(request.message.to_string()).size(LabelSize::Small)), + ) .children(request.actions.iter().enumerate().map(|(ix, action)| { let this_handle = cx.view().clone(); ui::Button::new(ix, action.title.clone()) @@ -263,10 +303,6 @@ impl Render for LanguageServerPrompt { }) })), ) - .child( - ui::IconButton::new("close", ui::IconName::Close) - .on_click(cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent))), - ) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 35c3b1e69ce5ee5c53830000f65cfe8741b1a5ce..8a2fb64b8b0a3ca233db6fd7f44d89154a5ba6e5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -638,7 +638,7 @@ impl Workspace { project::Event::LanguageServerPrompt(request) => { let mut hasher = DefaultHasher::new(); - request.message.as_str().hash(&mut hasher); + request.lsp_name.as_str().hash(&mut hasher); let id = hasher.finish(); this.show_notification(id as usize, cx, |cx| {