Detailed changes
@@ -127,6 +127,16 @@ impl LanguageServerState {
return menu;
};
+ let server_versions = self
+ .lsp_store
+ .update(cx, |lsp_store, _| {
+ lsp_store
+ .language_server_statuses()
+ .map(|(server_id, status)| (server_id, status.server_version.clone()))
+ .collect::<HashMap<_, _>>()
+ })
+ .unwrap_or_default();
+
let mut first_button_encountered = false;
for item in &self.items {
if let LspMenuItem::ToggleServersButton { restart } = item {
@@ -254,6 +264,22 @@ impl LanguageServerState {
};
let server_name = server_info.name.clone();
+ let server_version = server_versions
+ .get(&server_info.id)
+ .and_then(|version| version.clone());
+
+ let tooltip_text = match (&server_version, &message) {
+ (None, None) => None,
+ (Some(version), None) => {
+ Some(SharedString::from(format!("Version: {}", version.as_ref())))
+ }
+ (None, Some(message)) => Some(message.clone()),
+ (Some(version), Some(message)) => Some(SharedString::from(format!(
+ "Version: {}\n\n{}",
+ version.as_ref(),
+ message.as_ref()
+ ))),
+ };
menu = menu.item(ContextMenuItem::custom_entry(
move |_, _| {
h_flex()
@@ -355,11 +381,11 @@ impl LanguageServerState {
}
}
},
- message.map(|server_message| {
+ tooltip_text.map(|tooltip_text| {
DocumentationAside::new(
DocumentationSide::Right,
- DocumentationEdge::Bottom,
- Rc::new(move |_| Label::new(server_message.clone()).into_any_element()),
+ DocumentationEdge::Top,
+ Rc::new(move |_| Label::new(tooltip_text.clone()).into_any_element()),
)
}),
));
@@ -330,6 +330,8 @@ impl LspLogView {
let server_info = format!(
"* Server: {NAME} (id {ID})
+* Version: {VERSION}
+
* Binary: {BINARY}
* Registered workspace folders:
@@ -340,6 +342,12 @@ impl LspLogView {
* Configuration: {CONFIGURATION}",
NAME = info.status.name,
ID = info.id,
+ VERSION = info
+ .status
+ .server_version
+ .as_ref()
+ .map(|version| version.as_ref())
+ .unwrap_or("Unknown"),
BINARY = info
.status
.binary
@@ -1334,6 +1342,7 @@ impl ServerInfo {
capabilities: server.capabilities(),
status: LanguageServerStatus {
name: server.name(),
+ server_version: server.version(),
pending_work: Default::default(),
has_pending_diagnostic_updates: false,
progress_tokens: Default::default(),
@@ -89,6 +89,7 @@ pub struct LanguageServer {
outbound_tx: channel::Sender<String>,
notification_tx: channel::Sender<NotificationSerializer>,
name: LanguageServerName,
+ version: Option<SharedString>,
process_name: Arc<str>,
binary: LanguageServerBinary,
capabilities: RwLock<ServerCapabilities>,
@@ -501,6 +502,7 @@ impl LanguageServer {
response_handlers,
io_handlers,
name: server_name,
+ version: None,
process_name: binary
.path
.file_name()
@@ -925,6 +927,7 @@ impl LanguageServer {
)
})?;
if let Some(info) = response.server_info {
+ self.version = info.version.map(SharedString::from);
self.process_name = info.name.into();
}
self.capabilities = RwLock::new(response.capabilities);
@@ -1155,6 +1158,11 @@ impl LanguageServer {
self.name.clone()
}
+ /// Get the version of the running language server.
+ pub fn version(&self) -> Option<SharedString> {
+ self.version.clone()
+ }
+
pub fn process_name(&self) -> &str {
&self.process_name
}
@@ -3864,6 +3864,7 @@ pub enum LspStoreEvent {
#[derive(Clone, Debug, Serialize)]
pub struct LanguageServerStatus {
pub name: LanguageServerName,
+ pub server_version: Option<SharedString>,
pub pending_work: BTreeMap<ProgressToken, LanguageServerProgress>,
pub has_pending_diagnostic_updates: bool,
pub progress_tokens: HashSet<ProgressToken>,
@@ -8354,6 +8355,7 @@ impl LspStore {
server_id,
LanguageServerStatus {
name,
+ server_version: None,
pending_work: Default::default(),
has_pending_diagnostic_updates: false,
progress_tokens: Default::default(),
@@ -9389,6 +9391,7 @@ impl LspStore {
server_id,
LanguageServerStatus {
name: server_name.clone(),
+ server_version: None,
pending_work: Default::default(),
has_pending_diagnostic_updates: false,
progress_tokens: Default::default(),
@@ -11419,6 +11422,7 @@ impl LspStore {
server_id,
LanguageServerStatus {
name: language_server.name(),
+ server_version: language_server.version(),
pending_work: Default::default(),
has_pending_diagnostic_updates: false,
progress_tokens: Default::default(),
@@ -893,39 +893,57 @@ impl ContextMenu {
entry_render,
handler,
selectable,
+ documentation_aside,
..
} => {
let handler = handler.clone();
let menu = cx.entity().downgrade();
let selectable = *selectable;
- ListItem::new(ix)
- .inset(true)
- .toggle_state(if selectable {
- Some(ix) == self.selected_index
- } else {
- false
+
+ div()
+ .id(("context-menu-child", ix))
+ .when_some(documentation_aside.clone(), |this, documentation_aside| {
+ this.occlude()
+ .on_hover(cx.listener(move |menu, hovered, _, cx| {
+ if *hovered {
+ menu.documentation_aside = Some((ix, documentation_aside.clone()));
+ } else if matches!(menu.documentation_aside, Some((id, _)) if id == ix)
+ {
+ menu.documentation_aside = None;
+ }
+ cx.notify();
+ }))
})
- .selectable(selectable)
- .when(selectable, |item| {
- item.on_click({
- let context = self.action_context.clone();
- let keep_open_on_confirm = self.keep_open_on_confirm;
- move |_, window, cx| {
- handler(context.as_ref(), window, cx);
- menu.update(cx, |menu, cx| {
- menu.clicked = true;
-
- if keep_open_on_confirm {
- menu.rebuild(window, cx);
- } else {
- cx.emit(DismissEvent);
+ .child(
+ ListItem::new(ix)
+ .inset(true)
+ .toggle_state(if selectable {
+ Some(ix) == self.selected_index
+ } else {
+ false
+ })
+ .selectable(selectable)
+ .when(selectable, |item| {
+ item.on_click({
+ let context = self.action_context.clone();
+ let keep_open_on_confirm = self.keep_open_on_confirm;
+ move |_, window, cx| {
+ handler(context.as_ref(), window, cx);
+ menu.update(cx, |menu, cx| {
+ menu.clicked = true;
+
+ if keep_open_on_confirm {
+ menu.rebuild(window, cx);
+ } else {
+ cx.emit(DismissEvent);
+ }
+ })
+ .ok();
}
})
- .ok();
- }
- })
- })
- .child(entry_render(window, cx))
+ })
+ .child(entry_render(window, cx)),
+ )
.into_any_element()
}
}