1use crate::{ItemViewHandle, Settings, StatusItemView};
2use futures::StreamExt;
3use gpui::{
4 action, elements::*, platform::CursorStyle, Entity, MutableAppContext, RenderContext, View,
5 ViewContext,
6};
7use language::{LanguageRegistry, LanguageServerBinaryStatus};
8use postage::watch;
9use std::sync::Arc;
10
11action!(DismissErrorMessage);
12
13pub struct LspStatus {
14 settings_rx: watch::Receiver<Settings>,
15 checking_for_update: Vec<String>,
16 downloading: Vec<String>,
17 failed: Vec<String>,
18}
19
20pub fn init(cx: &mut MutableAppContext) {
21 cx.add_action(LspStatus::dismiss_error_message);
22}
23
24impl LspStatus {
25 pub fn new(
26 languages: Arc<LanguageRegistry>,
27 settings_rx: watch::Receiver<Settings>,
28 cx: &mut ViewContext<Self>,
29 ) -> Self {
30 let mut status_events = languages.language_server_binary_statuses();
31 cx.spawn_weak(|this, mut cx| async move {
32 while let Some((language, event)) = status_events.next().await {
33 if let Some(this) = this.upgrade(&cx) {
34 this.update(&mut cx, |this, cx| {
35 for vector in [
36 &mut this.checking_for_update,
37 &mut this.downloading,
38 &mut this.failed,
39 ] {
40 vector.retain(|name| name != language.name().as_ref());
41 }
42
43 match event {
44 LanguageServerBinaryStatus::CheckingForUpdate => {
45 this.checking_for_update.push(language.name().to_string());
46 }
47 LanguageServerBinaryStatus::Downloading => {
48 this.downloading.push(language.name().to_string());
49 }
50 LanguageServerBinaryStatus::Failed => {
51 this.failed.push(language.name().to_string());
52 }
53 LanguageServerBinaryStatus::Downloaded
54 | LanguageServerBinaryStatus::Cached => {}
55 }
56
57 cx.notify();
58 });
59 } else {
60 break;
61 }
62 }
63 })
64 .detach();
65 Self {
66 settings_rx,
67 checking_for_update: Default::default(),
68 downloading: Default::default(),
69 failed: Default::default(),
70 }
71 }
72
73 fn dismiss_error_message(&mut self, _: &DismissErrorMessage, cx: &mut ViewContext<Self>) {
74 self.failed.clear();
75 cx.notify();
76 }
77}
78
79impl Entity for LspStatus {
80 type Event = ();
81}
82
83impl View for LspStatus {
84 fn ui_name() -> &'static str {
85 "LspStatus"
86 }
87
88 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
89 let theme = &self.settings_rx.borrow().theme;
90 if !self.downloading.is_empty() {
91 Label::new(
92 format!(
93 "Downloading {} language server{}...",
94 self.downloading.join(", "),
95 if self.downloading.len() > 1 { "s" } else { "" }
96 ),
97 theme.workspace.status_bar.lsp_message.clone(),
98 )
99 .boxed()
100 } else if !self.checking_for_update.is_empty() {
101 Label::new(
102 format!(
103 "Checking for updates to {} language server{}...",
104 self.checking_for_update.join(", "),
105 if self.checking_for_update.len() > 1 {
106 "s"
107 } else {
108 ""
109 }
110 ),
111 theme.workspace.status_bar.lsp_message.clone(),
112 )
113 .boxed()
114 } else if !self.failed.is_empty() {
115 MouseEventHandler::new::<Self, _, _>(0, cx, |_, _| {
116 Label::new(
117 format!(
118 "Failed to download {} language server{}. Click to dismiss.",
119 self.failed.join(", "),
120 if self.failed.len() > 1 { "s" } else { "" }
121 ),
122 theme.workspace.status_bar.lsp_message.clone(),
123 )
124 .boxed()
125 })
126 .with_cursor_style(CursorStyle::PointingHand)
127 .on_click(|cx| cx.dispatch_action(DismissErrorMessage))
128 .boxed()
129 } else {
130 Empty::new().boxed()
131 }
132 }
133}
134
135impl StatusItemView for LspStatus {
136 fn set_active_pane_item(&mut self, _: Option<&dyn ItemViewHandle>, _: &mut ViewContext<Self>) {}
137}