Cargo.lock 🔗
@@ -9165,7 +9165,6 @@ dependencies = [
"collections",
"copilot",
"editor",
- "feature_flags",
"futures 0.3.31",
"gpui",
"itertools 0.14.0",
Kirill Bulatov created
Also
* remove the feature gate
* open buffers with an error when no logs are present
* adjust the hover text to indicate that difference
<img width="480" height="380" alt="image"
src="https://github.com/user-attachments/assets/6b2350fc-5121-4b1e-bc22-503d964531a2"
/>
Release Notes:
- N/A
Cargo.lock | 1
crates/activity_indicator/src/activity_indicator.rs | 4
crates/language_tools/Cargo.toml | 1
crates/language_tools/src/lsp_tool.rs | 592 +++++++++-----
4 files changed, 357 insertions(+), 241 deletions(-)
@@ -9165,7 +9165,6 @@ dependencies = [
"collections",
"copilot",
"editor",
- "feature_flags",
"futures 0.3.31",
"gpui",
"itertools 0.14.0",
@@ -231,7 +231,6 @@ impl ActivityIndicator {
status,
} => {
let create_buffer = project.update(cx, |project, cx| project.create_buffer(cx));
- let project = project.clone();
let status = status.clone();
let server_name = server_name.clone();
cx.spawn_in(window, async move |workspace, cx| {
@@ -247,8 +246,7 @@ impl ActivityIndicator {
workspace.update_in(cx, |workspace, window, cx| {
workspace.add_item_to_active_pane(
Box::new(cx.new(|cx| {
- let mut editor =
- Editor::for_buffer(buffer, Some(project.clone()), window, cx);
+ let mut editor = Editor::for_buffer(buffer, None, window, cx);
editor.set_read_only(true);
editor
})),
@@ -18,7 +18,6 @@ client.workspace = true
collections.workspace = true
copilot.workspace = true
editor.workspace = true
-feature_flags.workspace = true
futures.workspace = true
gpui.workspace = true
itertools.workspace = true
@@ -1,13 +1,17 @@
-use std::{collections::hash_map, path::PathBuf, rc::Rc, time::Duration};
+use std::{
+ collections::{BTreeMap, HashMap},
+ path::{Path, PathBuf},
+ rc::Rc,
+ time::Duration,
+};
use client::proto;
-use collections::{HashMap, HashSet};
+use collections::HashSet;
use editor::{Editor, EditorEvent};
-use feature_flags::FeatureFlagAppExt as _;
use gpui::{Corner, Entity, Subscription, Task, WeakEntity, actions};
-use language::{BinaryStatus, BufferId, LocalFile, ServerHealth};
+use language::{BinaryStatus, BufferId, ServerHealth};
use lsp::{LanguageServerId, LanguageServerName, LanguageServerSelector};
-use project::{LspStore, LspStoreEvent, project_settings::ProjectSettings};
+use project::{LspStore, LspStoreEvent, Worktree, project_settings::ProjectSettings};
use settings::{Settings as _, SettingsStore};
use ui::{
Context, ContextMenu, ContextMenuEntry, ContextMenuItem, DocumentationAside, DocumentationSide,
@@ -36,8 +40,7 @@ pub struct LspTool {
#[derive(Debug)]
struct LanguageServerState {
- items: Vec<LspItem>,
- other_servers_start_index: Option<usize>,
+ items: Vec<LspMenuItem>,
workspace: WeakEntity<Workspace>,
lsp_store: WeakEntity<LspStore>,
active_editor: Option<ActiveEditor>,
@@ -63,8 +66,13 @@ impl std::fmt::Debug for ActiveEditor {
struct LanguageServers {
health_statuses: HashMap<LanguageServerId, LanguageServerHealthStatus>,
binary_statuses: HashMap<LanguageServerName, LanguageServerBinaryStatus>,
- servers_per_buffer_abs_path:
- HashMap<PathBuf, HashMap<LanguageServerId, Option<LanguageServerName>>>,
+ servers_per_buffer_abs_path: HashMap<PathBuf, ServersForPath>,
+}
+
+#[derive(Debug, Clone)]
+struct ServersForPath {
+ servers: HashMap<LanguageServerId, Option<LanguageServerName>>,
+ worktree: Option<WeakEntity<Worktree>>,
}
#[derive(Debug, Clone)]
@@ -120,8 +128,8 @@ impl LanguageServerState {
};
let mut first_button_encountered = false;
- for (i, item) in self.items.iter().enumerate() {
- if let LspItem::ToggleServersButton { restart } = item {
+ for item in &self.items {
+ if let LspMenuItem::ToggleServersButton { restart } = item {
let label = if *restart {
"Restart All Servers"
} else {
@@ -140,22 +148,19 @@ impl LanguageServerState {
};
let project = workspace.read(cx).project().clone();
let buffer_store = project.read(cx).buffer_store().clone();
- let worktree_store = project.read(cx).worktree_store();
-
let buffers = state
.read(cx)
.language_servers
.servers_per_buffer_abs_path
- .keys()
- .filter_map(|abs_path| {
- worktree_store.read(cx).find_worktree(abs_path, cx)
- })
- .filter_map(|(worktree, relative_path)| {
- let entry =
- worktree.read(cx).entry_for_path(&relative_path)?;
- project.read(cx).path_for_entry(entry.id, cx)
- })
- .filter_map(|project_path| {
+ .iter()
+ .filter_map(|(abs_path, servers)| {
+ let worktree =
+ servers.worktree.as_ref()?.upgrade()?.read(cx);
+ let relative_path =
+ abs_path.strip_prefix(&worktree.abs_path()).ok()?;
+ let entry = worktree.entry_for_path(&relative_path)?;
+ let project_path =
+ project.read(cx).path_for_entry(entry.id, cx)?;
buffer_store.read(cx).get_by_path(&project_path)
})
.collect();
@@ -165,13 +170,16 @@ impl LanguageServerState {
.iter()
// Do not try to use IDs as we have stopped all servers already, when allowing to restart them all
.flat_map(|item| match item {
- LspItem::ToggleServersButton { .. } => None,
- LspItem::WithHealthCheck(_, status, ..) => Some(
- LanguageServerSelector::Name(status.name.clone()),
- ),
- LspItem::WithBinaryStatus(_, server_name, ..) => Some(
- LanguageServerSelector::Name(server_name.clone()),
+ LspMenuItem::Header { .. } => None,
+ LspMenuItem::ToggleServersButton { .. } => None,
+ LspMenuItem::WithHealthCheck { health, .. } => Some(
+ LanguageServerSelector::Name(health.name.clone()),
),
+ LspMenuItem::WithBinaryStatus {
+ server_name, ..
+ } => Some(LanguageServerSelector::Name(
+ server_name.clone(),
+ )),
})
.collect();
lsp_store.restart_language_servers_for_buffers(
@@ -190,13 +198,17 @@ impl LanguageServerState {
}
menu = menu.item(button);
continue;
- };
+ } else if let LspMenuItem::Header { header, separator } = item {
+ menu = menu
+ .when(*separator, |menu| menu.separator())
+ .when_some(header.as_ref(), |menu, header| menu.header(header));
+ continue;
+ }
let Some(server_info) = item.server_info() else {
continue;
};
- let workspace = self.workspace.clone();
let server_selector = server_info.server_selector();
// TODO currently, Zed remote does not work well with the LSP logs
// https://github.com/zed-industries/zed/issues/28557
@@ -205,6 +217,7 @@ impl LanguageServerState {
let status_color = server_info
.binary_status
+ .as_ref()
.and_then(|binary_status| match binary_status.status {
BinaryStatus::None => None,
BinaryStatus::CheckingForUpdate
@@ -223,17 +236,20 @@ impl LanguageServerState {
})
.unwrap_or(Color::Success);
- if self
- .other_servers_start_index
- .is_some_and(|index| index == i)
- {
- menu = menu.separator().header("Other Buffers");
- }
-
- if i == 0 && self.other_servers_start_index.is_some() {
- menu = menu.header("Current Buffer");
- }
+ let message = server_info
+ .message
+ .as_ref()
+ .or_else(|| server_info.binary_status.as_ref()?.message.as_ref())
+ .cloned();
+ let hover_label = if has_logs {
+ Some("View Logs")
+ } else if message.is_some() {
+ Some("View Message")
+ } else {
+ None
+ };
+ let server_name = server_info.name.clone();
menu = menu.item(ContextMenuItem::custom_entry(
move |_, _| {
h_flex()
@@ -245,42 +261,99 @@ impl LanguageServerState {
h_flex()
.gap_2()
.child(Indicator::dot().color(status_color))
- .child(Label::new(server_info.name.0.clone())),
- )
- .child(
- h_flex()
- .visible_on_hover("menu_item")
- .child(
- Label::new("View Logs")
- .size(LabelSize::Small)
- .color(Color::Muted),
- )
- .child(
- Icon::new(IconName::ChevronRight)
- .size(IconSize::Small)
- .color(Color::Muted),
- ),
+ .child(Label::new(server_name.0.clone())),
)
+ .when_some(hover_label, |div, hover_label| {
+ div.child(
+ h_flex()
+ .visible_on_hover("menu_item")
+ .child(
+ Label::new(hover_label)
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ )
+ .child(
+ Icon::new(IconName::ChevronRight)
+ .size(IconSize::Small)
+ .color(Color::Muted),
+ ),
+ )
+ })
.into_any_element()
},
{
let lsp_logs = lsp_logs.clone();
+ let message = message.clone();
+ let server_selector = server_selector.clone();
+ let server_name = server_info.name.clone();
+ let workspace = self.workspace.clone();
move |window, cx| {
- if !has_logs {
+ if has_logs {
+ lsp_logs.update(cx, |lsp_logs, cx| {
+ lsp_logs.open_server_trace(
+ workspace.clone(),
+ server_selector.clone(),
+ window,
+ cx,
+ );
+ });
+ } else if let Some(message) = &message {
+ let Some(create_buffer) = workspace
+ .update(cx, |workspace, cx| {
+ workspace
+ .project()
+ .update(cx, |project, cx| project.create_buffer(cx))
+ })
+ .ok()
+ else {
+ return;
+ };
+
+ let window = window.window_handle();
+ let workspace = workspace.clone();
+ let message = message.clone();
+ let server_name = server_name.clone();
+ cx.spawn(async move |cx| {
+ let buffer = create_buffer.await?;
+ buffer.update(cx, |buffer, cx| {
+ buffer.edit(
+ [(
+ 0..0,
+ format!("Language server {server_name}:\n\n{message}"),
+ )],
+ None,
+ cx,
+ );
+ buffer.set_capability(language::Capability::ReadOnly, cx);
+ })?;
+
+ workspace.update(cx, |workspace, cx| {
+ window.update(cx, |_, window, cx| {
+ workspace.add_item_to_active_pane(
+ Box::new(cx.new(|cx| {
+ let mut editor =
+ Editor::for_buffer(buffer, None, window, cx);
+ editor.set_read_only(true);
+ editor
+ })),
+ None,
+ true,
+ window,
+ cx,
+ );
+ })
+ })??;
+
+ anyhow::Ok(())
+ })
+ .detach();
+ } else {
cx.propagate();
return;
}
- lsp_logs.update(cx, |lsp_logs, cx| {
- lsp_logs.open_server_trace(
- workspace.clone(),
- server_selector.clone(),
- window,
- cx,
- );
- });
}
},
- server_info.message.map(|server_message| {
+ message.map(|server_message| {
DocumentationAside::new(
DocumentationSide::Right,
Rc::new(move |_| Label::new(server_message.clone()).into_any_element()),
@@ -345,81 +418,95 @@ impl LanguageServers {
#[derive(Debug)]
enum ServerData<'a> {
- WithHealthCheck(
- LanguageServerId,
- &'a LanguageServerHealthStatus,
- Option<&'a LanguageServerBinaryStatus>,
- ),
- WithBinaryStatus(
- Option<LanguageServerId>,
- &'a LanguageServerName,
- &'a LanguageServerBinaryStatus,
- ),
+ WithHealthCheck {
+ server_id: LanguageServerId,
+ health: &'a LanguageServerHealthStatus,
+ binary_status: Option<&'a LanguageServerBinaryStatus>,
+ },
+ WithBinaryStatus {
+ server_id: Option<LanguageServerId>,
+ server_name: &'a LanguageServerName,
+ binary_status: &'a LanguageServerBinaryStatus,
+ },
}
#[derive(Debug)]
-enum LspItem {
- WithHealthCheck(
- LanguageServerId,
- LanguageServerHealthStatus,
- Option<LanguageServerBinaryStatus>,
- ),
- WithBinaryStatus(
- Option<LanguageServerId>,
- LanguageServerName,
- LanguageServerBinaryStatus,
- ),
+enum LspMenuItem {
+ WithHealthCheck {
+ server_id: LanguageServerId,
+ health: LanguageServerHealthStatus,
+ binary_status: Option<LanguageServerBinaryStatus>,
+ },
+ WithBinaryStatus {
+ server_id: Option<LanguageServerId>,
+ server_name: LanguageServerName,
+ binary_status: LanguageServerBinaryStatus,
+ },
ToggleServersButton {
restart: bool,
},
+ Header {
+ header: Option<SharedString>,
+ separator: bool,
+ },
}
-impl LspItem {
+impl LspMenuItem {
fn server_info(&self) -> Option<ServerInfo> {
match self {
- LspItem::ToggleServersButton { .. } => None,
- LspItem::WithHealthCheck(
- language_server_id,
- language_server_health_status,
- language_server_binary_status,
- ) => Some(ServerInfo {
- name: language_server_health_status.name.clone(),
- id: Some(*language_server_id),
- health: language_server_health_status.health(),
- binary_status: language_server_binary_status.clone(),
- message: language_server_health_status.message(),
+ Self::Header { .. } => None,
+ Self::ToggleServersButton { .. } => None,
+ Self::WithHealthCheck {
+ server_id,
+ health,
+ binary_status,
+ ..
+ } => Some(ServerInfo {
+ name: health.name.clone(),
+ id: Some(*server_id),
+ health: health.health(),
+ binary_status: binary_status.clone(),
+ message: health.message(),
}),
- LspItem::WithBinaryStatus(
+ Self::WithBinaryStatus {
server_id,
- language_server_name,
- language_server_binary_status,
- ) => Some(ServerInfo {
- name: language_server_name.clone(),
+ server_name,
+ binary_status,
+ ..
+ } => Some(ServerInfo {
+ name: server_name.clone(),
id: *server_id,
health: None,
- binary_status: Some(language_server_binary_status.clone()),
- message: language_server_binary_status.message.clone(),
+ binary_status: Some(binary_status.clone()),
+ message: binary_status.message.clone(),
}),
}
}
}
impl ServerData<'_> {
- fn name(&self) -> &LanguageServerName {
- match self {
- Self::WithHealthCheck(_, state, _) => &state.name,
- Self::WithBinaryStatus(_, name, ..) => name,
- }
- }
-
- fn into_lsp_item(self) -> LspItem {
+ fn into_lsp_item(self) -> LspMenuItem {
match self {
- Self::WithHealthCheck(id, name, status) => {
- LspItem::WithHealthCheck(id, name.clone(), status.cloned())
- }
- Self::WithBinaryStatus(server_id, name, status) => {
- LspItem::WithBinaryStatus(server_id, name.clone(), status.clone())
- }
+ Self::WithHealthCheck {
+ server_id,
+ health,
+ binary_status,
+ ..
+ } => LspMenuItem::WithHealthCheck {
+ server_id,
+ health: health.clone(),
+ binary_status: binary_status.cloned(),
+ },
+ Self::WithBinaryStatus {
+ server_id,
+ server_name,
+ binary_status,
+ ..
+ } => LspMenuItem::WithBinaryStatus {
+ server_id,
+ server_name: server_name.clone(),
+ binary_status: binary_status.clone(),
+ },
}
}
}
@@ -452,7 +539,6 @@ impl LspTool {
let state = cx.new(|_| LanguageServerState {
workspace: workspace.weak_handle(),
items: Vec::new(),
- other_servers_start_index: None,
lsp_store: lsp_store.downgrade(),
active_editor: None,
language_servers: LanguageServers::default(),
@@ -542,13 +628,28 @@ impl LspTool {
message: proto::update_language_server::Variant::RegisteredForBuffer(update),
..
} => {
- self.server_state.update(cx, |state, _| {
- state
+ self.server_state.update(cx, |state, cx| {
+ let Ok(worktree) = state.workspace.update(cx, |workspace, cx| {
+ workspace
+ .project()
+ .read(cx)
+ .find_worktree(Path::new(&update.buffer_abs_path), cx)
+ .map(|(worktree, _)| worktree.downgrade())
+ }) else {
+ return;
+ };
+ let entry = state
.language_servers
.servers_per_buffer_abs_path
.entry(PathBuf::from(&update.buffer_abs_path))
- .or_default()
- .insert(*language_server_id, name.clone());
+ .or_insert_with(|| ServersForPath {
+ servers: HashMap::default(),
+ worktree: worktree.clone(),
+ });
+ entry.servers.insert(*language_server_id, name.clone());
+ if worktree.is_some() {
+ entry.worktree = worktree;
+ }
});
updated = true;
}
@@ -562,94 +663,95 @@ impl LspTool {
fn regenerate_items(&mut self, cx: &mut App) {
self.server_state.update(cx, |state, cx| {
- let editor_buffers = state
+ let active_worktrees = state
.active_editor
.as_ref()
- .map(|active_editor| active_editor.editor_buffers.clone())
- .unwrap_or_default();
- let editor_buffer_paths = editor_buffers
- .iter()
- .filter_map(|buffer_id| {
- let buffer_path = state
- .lsp_store
- .update(cx, |lsp_store, cx| {
- Some(
- project::File::from_dyn(
- lsp_store
- .buffer_store()
- .read(cx)
- .get(*buffer_id)?
- .read(cx)
- .file(),
- )?
- .abs_path(cx),
- )
+ .into_iter()
+ .flat_map(|active_editor| {
+ active_editor
+ .editor
+ .upgrade()
+ .into_iter()
+ .flat_map(|active_editor| {
+ active_editor
+ .read(cx)
+ .buffer()
+ .read(cx)
+ .all_buffers()
+ .into_iter()
+ .filter_map(|buffer| {
+ project::File::from_dyn(buffer.read(cx).file())
+ })
+ .map(|buffer_file| buffer_file.worktree.clone())
})
- .ok()??;
- Some(buffer_path)
})
- .collect::<Vec<_>>();
+ .collect::<HashSet<_>>();
- let mut servers_with_health_checks = HashSet::default();
- let mut server_ids_with_health_checks = HashSet::default();
- let mut buffer_servers =
- Vec::with_capacity(state.language_servers.health_statuses.len());
- let mut other_servers =
- Vec::with_capacity(state.language_servers.health_statuses.len());
- let buffer_server_ids = editor_buffer_paths
- .iter()
- .filter_map(|buffer_path| {
- state
- .language_servers
- .servers_per_buffer_abs_path
- .get(buffer_path)
- })
- .flatten()
- .fold(HashMap::default(), |mut acc, (server_id, name)| {
- match acc.entry(*server_id) {
- hash_map::Entry::Occupied(mut o) => {
- let old_name: &mut Option<&LanguageServerName> = o.get_mut();
- if old_name.is_none() {
- *old_name = name.as_ref();
- }
- }
- hash_map::Entry::Vacant(v) => {
- v.insert(name.as_ref());
+ let mut server_ids_to_worktrees =
+ HashMap::<LanguageServerId, Entity<Worktree>>::default();
+ let mut server_names_to_worktrees = HashMap::<
+ LanguageServerName,
+ HashSet<(Entity<Worktree>, LanguageServerId)>,
+ >::default();
+ for servers_for_path in state.language_servers.servers_per_buffer_abs_path.values() {
+ if let Some(worktree) = servers_for_path
+ .worktree
+ .as_ref()
+ .and_then(|worktree| worktree.upgrade())
+ {
+ for (server_id, server_name) in &servers_for_path.servers {
+ server_ids_to_worktrees.insert(*server_id, worktree.clone());
+ if let Some(server_name) = server_name {
+ server_names_to_worktrees
+ .entry(server_name.clone())
+ .or_default()
+ .insert((worktree.clone(), *server_id));
}
}
- acc
+ }
+ }
+
+ let mut servers_per_worktree = BTreeMap::<SharedString, Vec<ServerData>>::new();
+ let mut servers_without_worktree = Vec::<ServerData>::new();
+ let mut servers_with_health_checks = HashSet::default();
+
+ for (server_id, health) in &state.language_servers.health_statuses {
+ let worktree = server_ids_to_worktrees.get(server_id).or_else(|| {
+ let worktrees = server_names_to_worktrees.get(&health.name)?;
+ worktrees
+ .iter()
+ .find(|(worktree, _)| active_worktrees.contains(worktree))
+ .or_else(|| worktrees.iter().next())
+ .map(|(worktree, _)| worktree)
});
- for (server_id, server_state) in &state.language_servers.health_statuses {
- let binary_status = state
- .language_servers
- .binary_statuses
- .get(&server_state.name);
- servers_with_health_checks.insert(&server_state.name);
- server_ids_with_health_checks.insert(*server_id);
- if buffer_server_ids.contains_key(server_id) {
- buffer_servers.push(ServerData::WithHealthCheck(
- *server_id,
- server_state,
- binary_status,
- ));
- } else {
- other_servers.push(ServerData::WithHealthCheck(
- *server_id,
- server_state,
- binary_status,
- ));
+ servers_with_health_checks.insert(&health.name);
+ let worktree_name =
+ worktree.map(|worktree| SharedString::new(worktree.read(cx).root_name()));
+
+ let binary_status = state.language_servers.binary_statuses.get(&health.name);
+ let server_data = ServerData::WithHealthCheck {
+ server_id: *server_id,
+ health,
+ binary_status,
+ };
+ match worktree_name {
+ Some(worktree_name) => servers_per_worktree
+ .entry(worktree_name.clone())
+ .or_default()
+ .push(server_data),
+ None => servers_without_worktree.push(server_data),
}
}
let mut can_stop_all = !state.language_servers.health_statuses.is_empty();
let mut can_restart_all = state.language_servers.health_statuses.is_empty();
- for (server_name, status) in state
+ for (server_name, binary_status) in state
.language_servers
.binary_statuses
.iter()
.filter(|(name, _)| !servers_with_health_checks.contains(name))
{
- match status.status {
+ match binary_status.status {
BinaryStatus::None => {
can_restart_all = false;
can_stop_all |= true;
@@ -674,52 +776,73 @@ impl LspTool {
BinaryStatus::Failed { .. } => {}
}
- let matching_server_id = state
- .language_servers
- .servers_per_buffer_abs_path
- .iter()
- .filter(|(path, _)| editor_buffer_paths.contains(path))
- .flat_map(|(_, server_associations)| server_associations.iter())
- .find_map(|(id, name)| {
- if name.as_ref() == Some(server_name) {
- Some(*id)
- } else {
- None
+ match server_names_to_worktrees.get(server_name) {
+ Some(worktrees_for_name) => {
+ match worktrees_for_name
+ .iter()
+ .find(|(worktree, _)| active_worktrees.contains(worktree))
+ .or_else(|| worktrees_for_name.iter().next())
+ {
+ Some((worktree, server_id)) => {
+ let worktree_name =
+ SharedString::new(worktree.read(cx).root_name());
+ servers_per_worktree
+ .entry(worktree_name.clone())
+ .or_default()
+ .push(ServerData::WithBinaryStatus {
+ server_name,
+ binary_status,
+ server_id: Some(*server_id),
+ });
+ }
+ None => servers_without_worktree.push(ServerData::WithBinaryStatus {
+ server_name,
+ binary_status,
+ server_id: None,
+ }),
}
- });
- if let Some(server_id) = matching_server_id {
- buffer_servers.push(ServerData::WithBinaryStatus(
- Some(server_id),
+ }
+ None => servers_without_worktree.push(ServerData::WithBinaryStatus {
server_name,
- status,
- ));
- } else {
- other_servers.push(ServerData::WithBinaryStatus(None, server_name, status));
+ binary_status,
+ server_id: None,
+ }),
}
}
- buffer_servers.sort_by_key(|data| data.name().clone());
- other_servers.sort_by_key(|data| data.name().clone());
-
- let mut other_servers_start_index = None;
let mut new_lsp_items =
- Vec::with_capacity(buffer_servers.len() + other_servers.len() + 1);
- new_lsp_items.extend(buffer_servers.into_iter().map(ServerData::into_lsp_item));
- if !new_lsp_items.is_empty() {
- other_servers_start_index = Some(new_lsp_items.len());
+ Vec::with_capacity(servers_per_worktree.len() + servers_without_worktree.len() + 2);
+ for (worktree_name, worktree_servers) in servers_per_worktree {
+ if worktree_servers.is_empty() {
+ continue;
+ }
+ new_lsp_items.push(LspMenuItem::Header {
+ header: Some(worktree_name),
+ separator: false,
+ });
+ new_lsp_items.extend(worktree_servers.into_iter().map(ServerData::into_lsp_item));
+ }
+ if !servers_without_worktree.is_empty() {
+ new_lsp_items.push(LspMenuItem::Header {
+ header: Some(SharedString::from("Unknown worktree")),
+ separator: false,
+ });
+ new_lsp_items.extend(
+ servers_without_worktree
+ .into_iter()
+ .map(ServerData::into_lsp_item),
+ );
}
- new_lsp_items.extend(other_servers.into_iter().map(ServerData::into_lsp_item));
if !new_lsp_items.is_empty() {
if can_stop_all {
- new_lsp_items.push(LspItem::ToggleServersButton { restart: true });
- new_lsp_items.push(LspItem::ToggleServersButton { restart: false });
+ new_lsp_items.push(LspMenuItem::ToggleServersButton { restart: true });
+ new_lsp_items.push(LspMenuItem::ToggleServersButton { restart: false });
} else if can_restart_all {
- new_lsp_items.push(LspItem::ToggleServersButton { restart: true });
+ new_lsp_items.push(LspMenuItem::ToggleServersButton { restart: true });
}
}
state.items = new_lsp_items;
- state.other_servers_start_index = other_servers_start_index;
});
}
@@ -841,10 +964,7 @@ impl StatusItemView for LspTool {
impl Render for LspTool {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
- if !cx.is_staff()
- || self.server_state.read(cx).language_servers.is_empty()
- || self.lsp_menu.is_none()
- {
+ if self.server_state.read(cx).language_servers.is_empty() || self.lsp_menu.is_none() {
return div();
}
@@ -852,12 +972,12 @@ impl Render for LspTool {
let mut has_warnings = false;
let mut has_other_notifications = false;
let state = self.server_state.read(cx);
- for server in state.language_servers.health_statuses.values() {
- if let Some(binary_status) = &state.language_servers.binary_statuses.get(&server.name) {
- has_errors |= matches!(binary_status.status, BinaryStatus::Failed { .. });
- has_other_notifications |= binary_status.message.is_some();
- }
+ for binary_status in state.language_servers.binary_statuses.values() {
+ has_errors |= matches!(binary_status.status, BinaryStatus::Failed { .. });
+ has_other_notifications |= binary_status.message.is_some();
+ }
+ for server in state.language_servers.health_statuses.values() {
if let Some((message, health)) = &server.health {
has_other_notifications |= message.is_some();
match health {