Detailed changes
@@ -399,6 +399,8 @@ fn box_suffixes() -> Vec<(&'static str, TokenStream2, &'static str)> {
("72", quote! { rems(18.) }, "288px (18rem)"),
("80", quote! { rems(20.) }, "320px (20rem)"),
("96", quote! { rems(24.) }, "384px (24rem)"),
+ ("112", quote! { rems(28.) }, "448px (28rem)"),
+ ("128", quote! { rems(32.) }, "512px (32rem)"),
("auto", quote! { auto() }, "Auto"),
("px", quote! { px(1.) }, "1px"),
("full", quote! { relative(1.) }, "100%"),
@@ -228,6 +228,7 @@ pub struct LanguageServerPromptRequest {
pub level: PromptLevel,
pub message: String,
pub actions: Vec<MessageActionItem>,
+ pub lsp_name: String,
response_channel: Sender<MessageActionItem>,
}
@@ -3010,6 +3011,7 @@ impl Project {
cx.update(|cx| adapter.workspace_configuration(worktree_path, cx))?;
let language_server = pending_server.task.await?;
+ let name = language_server.name();
language_server
.on_notification::<lsp::notification::PublishDiagnostics, _>({
let adapter = adapter.clone();
@@ -3148,8 +3150,10 @@ impl Project {
language_server
.on_request::<lsp::request::ShowMessageRequest, _, _>({
let this = this.clone();
+ let name = name.to_string();
move |params, mut cx| {
let this = this.clone();
+ let name = name.to_string();
async move {
if let Some(actions) = params.actions {
let (tx, mut rx) = smol::channel::bounded(1);
@@ -3162,6 +3166,7 @@ impl Project {
message: params.message,
actions,
response_channel: tx,
+ lsp_name: name.clone(),
};
if let Ok(_) = this.update(&mut cx, |_, cx| {
@@ -3199,6 +3204,7 @@ impl Project {
}
})
.detach();
+
let mut initialization_options = adapter.adapter.initialization_options();
match (&mut initialization_options, override_options) {
(Some(initialization_options), Some(override_options)) => {
@@ -7,6 +7,7 @@ use crate::prelude::*;
pub enum LabelSize {
#[default]
Default,
+ Large,
Small,
XSmall,
}
@@ -97,6 +98,7 @@ impl RenderOnce for LabelLike {
)
})
.map(|this| match self.size {
+ LabelSize::Large => this.text_ui_lg(),
LabelSize::Default => this.text_ui(),
LabelSize::Small => this.text_ui_sm(),
LabelSize::XSmall => this.text_ui_xs(),
@@ -35,6 +35,17 @@ pub trait StyledExt: Styled + Sized {
self.text_size(size.rems())
}
+ /// The large size for UI text.
+ ///
+ /// `1rem` or `16px` at the default scale of `1rem` = `16px`.
+ ///
+ /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
+ ///
+ /// Use `text_ui` for regular-sized text.
+ fn text_ui_lg(self) -> Self {
+ self.text_size(UiTextSize::Large.rems())
+ }
+
/// The default size for UI text.
///
/// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
@@ -13,6 +13,13 @@ pub enum UiTextSize {
/// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
#[default]
Default,
+ /// The large size for UI text.
+ ///
+ /// `1rem` or `16px` at the default scale of `1rem` = `16px`.
+ ///
+ /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
+ Large,
+
/// The small size for UI text.
///
/// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
@@ -31,6 +38,7 @@ pub enum UiTextSize {
impl UiTextSize {
pub fn rems(self) -> Rems {
match self {
+ Self::Large => rems(16. / 16.),
Self::Default => rems(14. / 16.),
Self::Small => rems(12. / 16.),
Self::XSmall => rems(10. / 16.),
@@ -1,10 +1,14 @@
use crate::{Toast, Workspace};
use collections::HashMap;
use gpui::{
- AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, Global,
- PromptLevel, Render, Task, View, ViewContext, VisualContext, WindowContext,
+ svg, AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter,
+ Global, PromptLevel, Render, Task, View, ViewContext, VisualContext, WindowContext,
};
+use language::DiagnosticSeverity;
+
use std::{any::TypeId, ops::DerefMut};
+use ui::prelude::*;
+use util::ResultExt;
pub fn init(cx: &mut AppContext) {
cx.set_global(NotificationTracker::new());
@@ -168,6 +172,105 @@ impl Workspace {
}
}
+pub struct LanguageServerPrompt {
+ request: Option<project::LanguageServerPromptRequest>,
+}
+
+impl LanguageServerPrompt {
+ pub fn new(request: project::LanguageServerPromptRequest) -> Self {
+ Self {
+ request: Some(request),
+ }
+ }
+
+ async fn select_option(this: View<Self>, ix: usize, mut cx: AsyncWindowContext) {
+ util::async_maybe!({
+ let potential_future = this.update(&mut cx, |this, _| {
+ this.request.take().map(|request| request.respond(ix))
+ });
+
+ potential_future? // App Closed
+ .ok_or_else(|| anyhow::anyhow!("Response already sent"))?
+ .await
+ .ok_or_else(|| anyhow::anyhow!("Stream already closed"))?;
+
+ this.update(&mut cx, |_, cx| cx.emit(DismissEvent))?;
+
+ anyhow::Ok(())
+ })
+ .await
+ .log_err();
+ }
+}
+
+impl Render for LanguageServerPrompt {
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+ let Some(request) = &self.request else {
+ return div().id("language_server_prompt_notification");
+ };
+
+ h_flex()
+ .id("language_server_prompt_notification")
+ .elevation_3(cx)
+ .items_start()
+ .p_2()
+ .gap_2()
+ .w_full()
+ .child(
+ v_flex()
+ .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))
+ }
+ })
+ }),
+ )
+ .child(
+ Label::new(format!("{}:", request.lsp_name))
+ .size(LabelSize::Default),
+ ),
+ )
+ .child(Label::new(request.message.to_string()))
+ .children(request.actions.iter().enumerate().map(|(ix, action)| {
+ let this_handle = cx.view().clone();
+ ui::Button::new(ix, action.title.clone())
+ .size(ButtonSize::Large)
+ .on_click(move |_, cx| {
+ let this_handle = this_handle.clone();
+ cx.spawn(|cx| async move {
+ LanguageServerPrompt::select_option(this_handle, ix, cx).await
+ })
+ .detach()
+ })
+ })),
+ )
+ .child(
+ ui::IconButton::new("close", ui::IconName::Close)
+ .on_click(cx.listener(|_, _, cx| cx.emit(gpui::DismissEvent))),
+ )
+ }
+}
+
+impl EventEmitter<DismissEvent> for LanguageServerPrompt {}
+
pub mod simple_message_notification {
use gpui::{
div, DismissEvent, EventEmitter, InteractiveElement, ParentElement, Render, SharedString,
@@ -59,7 +59,10 @@ use std::{
any::TypeId,
borrow::Cow,
cell::RefCell,
- cmp, env,
+ cmp,
+ collections::hash_map::DefaultHasher,
+ env,
+ hash::{Hash, Hasher},
path::{Path, PathBuf},
rc::Rc,
sync::{atomic::AtomicUsize, Arc, Weak},
@@ -578,24 +581,13 @@ impl Workspace {
}),
project::Event::LanguageServerPrompt(request) => {
- let request = request.clone();
+ let mut hasher = DefaultHasher::new();
+ request.message.as_str().hash(&mut hasher);
+ let id = hasher.finish();
- cx.spawn(|_, mut cx| async move {
- let messages = request
- .actions
- .iter()
- .map(|action| action.title.as_str())
- .collect::<Vec<_>>();
- let index = cx
- .update(|cx| {
- cx.prompt(request.level, "", Some(&request.message), &messages)
- })?
- .await?;
- request.respond(index).await;
-
- Result::<(), anyhow::Error>::Ok(())
- })
- .detach()
+ this.show_notification(id as usize, cx, |cx| {
+ cx.new_view(|_| notifications::LanguageServerPrompt::new(request.clone()))
+ });
}
_ => {}
@@ -2759,7 +2751,7 @@ impl Workspace {
.z_index(100)
.right_3()
.bottom_3()
- .w_96()
+ .w_112()
.h_full()
.flex()
.flex_col()