Restructure LSP log view to show log messages in addition to RPC trace

This commit is contained in:
Max Brunsfeld 2023-06-07 14:48:08 -07:00
parent 78f9642ac2
commit 66f215cd13
5 changed files with 337 additions and 138 deletions

View file

@ -748,6 +748,15 @@ impl fmt::Display for LanguageServerId {
} }
} }
impl fmt::Debug for LanguageServer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LanguageServer")
.field("id", &self.server_id.0)
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl Drop for Subscription { impl Drop for Subscription {
fn drop(&mut self) { fn drop(&mut self) {
match self { match self {

View file

@ -1,4 +1,4 @@
use collections::{hash_map, HashMap}; use collections::HashMap;
use editor::Editor; use editor::Editor;
use futures::{channel::mpsc, StreamExt}; use futures::{channel::mpsc, StreamExt};
use gpui::{ use gpui::{
@ -12,28 +12,33 @@ use gpui::{
ViewHandle, WeakModelHandle, ViewHandle, WeakModelHandle,
}; };
use language::{Buffer, LanguageServerId, LanguageServerName}; use language::{Buffer, LanguageServerId, LanguageServerName};
use project::{Project, WorktreeId}; use project::{Project, Worktree};
use std::{borrow::Cow, sync::Arc}; use std::{borrow::Cow, sync::Arc};
use theme::{ui, Theme}; use theme::{ui, Theme};
use workspace::{ use workspace::{
item::{Item, ItemHandle}, item::{Item, ItemHandle},
ToolbarItemLocation, ToolbarItemView, Workspace, ToolbarItemLocation, ToolbarItemView, Workspace, WorkspaceCreated,
}; };
const SEND_LINE: &str = "// Send:\n"; const SEND_LINE: &str = "// Send:\n";
const RECEIVE_LINE: &str = "// Receive:\n"; const RECEIVE_LINE: &str = "// Receive:\n";
struct LogStore { struct LogStore {
projects: HashMap<WeakModelHandle<Project>, LogStoreProject>, projects: HashMap<WeakModelHandle<Project>, ProjectState>,
io_tx: mpsc::UnboundedSender<(WeakModelHandle<Project>, LanguageServerId, bool, String)>, io_tx: mpsc::UnboundedSender<(WeakModelHandle<Project>, LanguageServerId, bool, String)>,
} }
struct LogStoreProject { struct ProjectState {
servers: HashMap<LanguageServerId, LogStoreLanguageServer>, servers: HashMap<LanguageServerId, LanguageServerState>,
_subscription: gpui::Subscription, _subscriptions: [gpui::Subscription; 2],
} }
struct LogStoreLanguageServer { struct LanguageServerState {
log_buffer: ModelHandle<Buffer>,
rpc_state: Option<LanguageServerRpcState>,
}
struct LanguageServerRpcState {
buffer: ModelHandle<Buffer>, buffer: ModelHandle<Buffer>,
last_message_kind: Option<MessageKind>, last_message_kind: Option<MessageKind>,
_subscription: lsp::Subscription, _subscription: lsp::Subscription,
@ -42,6 +47,7 @@ struct LogStoreLanguageServer {
pub struct LspLogView { pub struct LspLogView {
log_store: ModelHandle<LogStore>, log_store: ModelHandle<LogStore>,
current_server_id: Option<LanguageServerId>, current_server_id: Option<LanguageServerId>,
is_showing_rpc_trace: bool,
editor: Option<ViewHandle<Editor>>, editor: Option<ViewHandle<Editor>>,
project: ModelHandle<Project>, project: ModelHandle<Project>,
} }
@ -49,7 +55,6 @@ pub struct LspLogView {
pub struct LspLogToolbarItemView { pub struct LspLogToolbarItemView {
log_view: Option<ViewHandle<LspLogView>>, log_view: Option<ViewHandle<LspLogView>>,
menu_open: bool, menu_open: bool,
project: ModelHandle<Project>,
} }
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
@ -58,10 +63,36 @@ enum MessageKind {
Receive, Receive,
} }
#[derive(Clone, Debug, PartialEq)]
struct LogMenuItem {
server_id: LanguageServerId,
server_name: LanguageServerName,
worktree: ModelHandle<Worktree>,
rpc_trace_enabled: bool,
rpc_trace_selected: bool,
logs_selected: bool,
}
actions!(log, [OpenLanguageServerLogs]); actions!(log, [OpenLanguageServerLogs]);
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
let log_set = cx.add_model(|cx| LogStore::new(cx)); let log_store = cx.add_model(|cx| LogStore::new(cx));
cx.subscribe_global::<WorkspaceCreated, _>({
let log_store = log_store.clone();
move |event, cx| {
let workspace = &event.0;
if let Some(workspace) = workspace.upgrade(cx) {
let project = workspace.read(cx).project().clone();
if project.read(cx).is_local() {
log_store.update(cx, |store, cx| {
store.add_project(&project, cx);
});
}
}
}
})
.detach();
cx.add_action( cx.add_action(
move |workspace: &mut Workspace, _: &OpenLanguageServerLogs, cx: _| { move |workspace: &mut Workspace, _: &OpenLanguageServerLogs, cx: _| {
@ -69,7 +100,7 @@ pub fn init(cx: &mut AppContext) {
if project.is_local() { if project.is_local() {
workspace.add_item( workspace.add_item(
Box::new(cx.add_view(|cx| { Box::new(cx.add_view(|cx| {
LspLogView::new(workspace.project().clone(), log_set.clone(), cx) LspLogView::new(workspace.project().clone(), log_store.clone(), cx)
})), })),
cx, cx,
); );
@ -100,34 +131,113 @@ impl LogStore {
this this
} }
pub fn has_enabled_logs_for_language_server( pub fn add_project(&mut self, project: &ModelHandle<Project>, cx: &mut ModelContext<Self>) {
use project::Event::*;
let weak_project = project.downgrade();
self.projects.insert(
weak_project,
ProjectState {
servers: HashMap::default(),
_subscriptions: [
cx.observe_release(&project, move |this, _, _| {
this.projects.remove(&weak_project);
}),
cx.subscribe(project, |this, project, event, cx| match event {
LanguageServerAdded(id) => {
this.add_language_server(&project, *id, cx);
}
LanguageServerRemoved(id) => {
this.remove_language_server(&project, *id, cx);
}
LanguageServerLog(id, message) => {
this.add_language_server_log(&project, *id, message, cx);
}
_ => {}
}),
],
},
);
}
fn add_language_server(
&mut self,
project: &ModelHandle<Project>,
id: LanguageServerId,
cx: &mut ModelContext<Self>,
) -> Option<ModelHandle<Buffer>> {
let project_state = self.projects.get_mut(&project.downgrade())?;
Some(
project_state
.servers
.entry(id)
.or_insert_with(|| {
cx.notify();
LanguageServerState {
rpc_state: None,
log_buffer: cx.add_model(|cx| Buffer::new(0, "", cx)).clone(),
}
})
.log_buffer
.clone(),
)
}
fn add_language_server_log(
&mut self,
project: &ModelHandle<Project>,
id: LanguageServerId,
message: &str,
cx: &mut ModelContext<Self>,
) -> Option<()> {
let buffer = self.add_language_server(&project, id, cx)?;
buffer.update(cx, |buffer, cx| {
let len = buffer.len();
let has_newline = message.ends_with("\n");
buffer.edit([(len..len, message)], None, cx);
if !has_newline {
let len = buffer.len();
buffer.edit([(len..len, "\n")], None, cx);
}
});
cx.notify();
Some(())
}
fn remove_language_server(
&mut self,
project: &ModelHandle<Project>,
id: LanguageServerId,
cx: &mut ModelContext<Self>,
) -> Option<()> {
let project_state = self.projects.get_mut(&project.downgrade())?;
project_state.servers.remove(&id);
cx.notify();
Some(())
}
pub fn log_buffer_for_server(
&self, &self,
project: &ModelHandle<Project>, project: &ModelHandle<Project>,
server_id: LanguageServerId, server_id: LanguageServerId,
) -> bool { ) -> Option<ModelHandle<Buffer>> {
self.projects let weak_project = project.downgrade();
.get(&project.downgrade()) let project_state = self.projects.get(&weak_project)?;
.map_or(false, |store| store.servers.contains_key(&server_id)) let server_state = project_state.servers.get(&server_id)?;
Some(server_state.log_buffer.clone())
} }
pub fn enable_logs_for_language_server( pub fn enable_rpc_trace_for_language_server(
&mut self, &mut self,
project: &ModelHandle<Project>, project: &ModelHandle<Project>,
server_id: LanguageServerId, server_id: LanguageServerId,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Option<ModelHandle<Buffer>> { ) -> Option<ModelHandle<Buffer>> {
let server = project.read(cx).language_server_for_id(server_id)?;
let weak_project = project.downgrade(); let weak_project = project.downgrade();
let project_logs = match self.projects.entry(weak_project) { let project_state = self.projects.get_mut(&weak_project)?;
hash_map::Entry::Occupied(entry) => entry.into_mut(), let server_state = project_state.servers.get_mut(&server_id)?;
hash_map::Entry::Vacant(entry) => entry.insert(LogStoreProject { let server = project.read(cx).language_server_for_id(server_id)?;
servers: HashMap::default(), let rpc_state = server_state.rpc_state.get_or_insert_with(|| {
_subscription: cx.observe_release(&project, move |this, _, _| {
this.projects.remove(&weak_project);
}),
}),
};
let server_log_state = project_logs.servers.entry(server_id).or_insert_with(|| {
let io_tx = self.io_tx.clone(); let io_tx = self.io_tx.clone();
let language = project.read(cx).languages().language_for_name("JSON"); let language = project.read(cx).languages().language_for_name("JSON");
let buffer = cx.add_model(|cx| Buffer::new(0, "", cx)); let buffer = cx.add_model(|cx| Buffer::new(0, "", cx));
@ -142,33 +252,30 @@ impl LogStore {
}) })
.detach(); .detach();
let project = project.downgrade(); LanguageServerRpcState {
LogStoreLanguageServer {
buffer, buffer,
last_message_kind: None, last_message_kind: None,
_subscription: server.on_io(move |is_received, json| { _subscription: server.on_io(move |is_received, json| {
io_tx io_tx
.unbounded_send((project, server_id, is_received, json.to_string())) .unbounded_send((weak_project, server_id, is_received, json.to_string()))
.ok(); .ok();
}), }),
} }
}); });
Some(server_log_state.buffer.clone()) Some(rpc_state.buffer.clone())
} }
pub fn disable_logs_for_language_server( pub fn disable_rpc_trace_for_language_server(
&mut self, &mut self,
project: &ModelHandle<Project>, project: &ModelHandle<Project>,
server_id: LanguageServerId, server_id: LanguageServerId,
_: &mut ModelContext<Self>, _: &mut ModelContext<Self>,
) { ) -> Option<()> {
let project = project.downgrade(); let project = project.downgrade();
if let Some(store) = self.projects.get_mut(&project) { let project_state = self.projects.get_mut(&project)?;
store.servers.remove(&server_id); let server_state = project_state.servers.get_mut(&server_id)?;
if store.servers.is_empty() { server_state.rpc_state.take();
self.projects.remove(&project); Some(())
}
}
} }
fn on_io( fn on_io(
@ -183,7 +290,9 @@ impl LogStore {
.projects .projects
.get_mut(&project)? .get_mut(&project)?
.servers .servers
.get_mut(&language_server_id)?; .get_mut(&language_server_id)?
.rpc_state
.as_mut()?;
state.buffer.update(cx, |buffer, cx| { state.buffer.update(cx, |buffer, cx| {
let kind = if is_received { let kind = if is_received {
MessageKind::Receive MessageKind::Receive
@ -209,23 +318,62 @@ impl LogStore {
impl LspLogView { impl LspLogView {
fn new( fn new(
project: ModelHandle<Project>, project: ModelHandle<Project>,
log_set: ModelHandle<LogStore>, log_store: ModelHandle<LogStore>,
_: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
Self { let server_id = log_store
.read(cx)
.projects
.get(&project.downgrade())
.and_then(|project| project.servers.keys().copied().next());
let mut this = Self {
project, project,
log_store: log_set, log_store,
editor: None, editor: None,
current_server_id: None, current_server_id: None,
is_showing_rpc_trace: false,
};
if let Some(server_id) = server_id {
this.show_logs_for_server(server_id, cx);
} }
this
}
fn menu_items<'a>(&'a self, cx: &'a AppContext) -> Option<Vec<LogMenuItem>> {
let log_store = self.log_store.read(cx);
let state = log_store.projects.get(&self.project.downgrade())?;
let mut rows = self
.project
.read(cx)
.language_servers()
.filter_map(|(server_id, language_server_name, worktree_id)| {
let worktree = self.project.read(cx).worktree_for_id(worktree_id, cx)?;
let state = state.servers.get(&server_id)?;
Some(LogMenuItem {
server_id,
server_name: language_server_name,
worktree,
rpc_trace_enabled: state.rpc_state.is_some(),
rpc_trace_selected: self.is_showing_rpc_trace
&& self.current_server_id == Some(server_id),
logs_selected: !self.is_showing_rpc_trace
&& self.current_server_id == Some(server_id),
})
})
.collect::<Vec<_>>();
rows.sort_by_key(|row| row.server_id);
rows.dedup_by_key(|row| row.server_id);
Some(rows)
} }
fn show_logs_for_server(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) { fn show_logs_for_server(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) {
let buffer = self.log_store.update(cx, |log_set, cx| { let buffer = self
log_set.enable_logs_for_language_server(&self.project, server_id, cx) .log_store
}); .read(cx)
.log_buffer_for_server(&self.project, server_id);
if let Some(buffer) = buffer { if let Some(buffer) = buffer {
self.current_server_id = Some(server_id); self.current_server_id = Some(server_id);
self.is_showing_rpc_trace = false;
self.editor = Some(cx.add_view(|cx| { self.editor = Some(cx.add_view(|cx| {
let mut editor = Editor::for_buffer(buffer, Some(self.project.clone()), cx); let mut editor = Editor::for_buffer(buffer, Some(self.project.clone()), cx);
editor.set_read_only(true); editor.set_read_only(true);
@ -236,7 +384,28 @@ impl LspLogView {
} }
} }
fn toggle_logging_for_server( fn show_rpc_trace_for_server(
&mut self,
server_id: LanguageServerId,
cx: &mut ViewContext<Self>,
) {
let buffer = self.log_store.update(cx, |log_set, cx| {
log_set.enable_rpc_trace_for_language_server(&self.project, server_id, cx)
});
if let Some(buffer) = buffer {
self.current_server_id = Some(server_id);
self.is_showing_rpc_trace = true;
self.editor = Some(cx.add_view(|cx| {
let mut editor = Editor::for_buffer(buffer, Some(self.project.clone()), cx);
editor.set_read_only(true);
editor.move_to_end(&Default::default(), cx);
editor
}));
cx.notify();
}
}
fn toggle_rpc_trace_for_server(
&mut self, &mut self,
server_id: LanguageServerId, server_id: LanguageServerId,
enabled: bool, enabled: bool,
@ -244,11 +413,15 @@ impl LspLogView {
) { ) {
self.log_store.update(cx, |log_store, cx| { self.log_store.update(cx, |log_store, cx| {
if enabled { if enabled {
log_store.enable_logs_for_language_server(&self.project, server_id, cx); log_store.enable_rpc_trace_for_language_server(&self.project, server_id, cx);
} else { } else {
log_store.disable_logs_for_language_server(&self.project, server_id, cx); log_store.disable_rpc_trace_for_language_server(&self.project, server_id, cx);
} }
}); });
if !enabled && Some(server_id) == self.current_server_id {
self.show_logs_for_server(server_id, cx);
cx.notify();
}
} }
} }
@ -305,28 +478,18 @@ impl View for LspLogToolbarItemView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> { fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
let theme = theme::current(cx).clone(); let theme = theme::current(cx).clone();
let Some(log_view) = self.log_view.as_ref() else { return Empty::new().into_any() }; let Some(log_view) = self.log_view.as_ref() else { return Empty::new().into_any() };
let project = self.project.read(cx);
let log_view = log_view.read(cx); let log_view = log_view.read(cx);
let log_store = log_view.log_store.read(cx);
let mut language_servers = project let menu_rows = self
.language_servers() .log_view
.map(|(id, name, worktree)| { .as_ref()
( .and_then(|view| view.read(cx).menu_items(cx))
id, .unwrap_or_default();
name,
worktree,
log_store.has_enabled_logs_for_language_server(&self.project, id),
)
})
.collect::<Vec<_>>();
language_servers.sort_by_key(|a| (a.0, a.2));
language_servers.dedup_by_key(|a| a.0);
let current_server_id = log_view.current_server_id; let current_server_id = log_view.current_server_id;
let current_server = current_server_id.and_then(|current_server_id| { let current_server = current_server_id.and_then(|current_server_id| {
if let Ok(ix) = language_servers.binary_search_by_key(&current_server_id, |e| e.0) { if let Ok(ix) = menu_rows.binary_search_by_key(&current_server_id, |e| e.server_id) {
Some(language_servers[ix].clone()) Some(menu_rows[ix].clone())
} else { } else {
None None
} }
@ -337,7 +500,6 @@ impl View for LspLogToolbarItemView {
Stack::new() Stack::new()
.with_child(Self::render_language_server_menu_header( .with_child(Self::render_language_server_menu_header(
current_server, current_server,
&self.project,
&theme, &theme,
cx, cx,
)) ))
@ -346,20 +508,18 @@ impl View for LspLogToolbarItemView {
Overlay::new( Overlay::new(
MouseEventHandler::<Menu, _>::new(0, cx, move |_, cx| { MouseEventHandler::<Menu, _>::new(0, cx, move |_, cx| {
Flex::column() Flex::column()
.with_children(language_servers.into_iter().filter_map( .with_children(menu_rows.into_iter().map(|row| {
|(id, name, worktree_id, logging_enabled)| { Self::render_language_server_menu_item(
Self::render_language_server_menu_item( row.server_id,
id, row.server_name,
name, row.worktree,
worktree_id, row.rpc_trace_enabled,
logging_enabled, row.logs_selected,
Some(id) == current_server_id, row.rpc_trace_selected,
&self.project, &theme,
&theme, cx,
cx, )
) }))
},
))
.contained() .contained()
.with_style(theme.context_menu.container) .with_style(theme.context_menu.container)
.constrained() .constrained()
@ -389,11 +549,10 @@ impl View for LspLogToolbarItemView {
} }
impl LspLogToolbarItemView { impl LspLogToolbarItemView {
pub fn new(project: ModelHandle<Project>) -> Self { pub fn new() -> Self {
Self { Self {
menu_open: false, menu_open: false,
log_view: None, log_view: None,
project,
} }
} }
@ -410,10 +569,9 @@ impl LspLogToolbarItemView {
) { ) {
if let Some(log_view) = &self.log_view { if let Some(log_view) = &self.log_view {
log_view.update(cx, |log_view, cx| { log_view.update(cx, |log_view, cx| {
log_view.toggle_logging_for_server(id, enabled, cx); log_view.toggle_rpc_trace_for_server(id, enabled, cx);
if !enabled && Some(id) == log_view.current_server_id { if !enabled && Some(id) == log_view.current_server_id {
log_view.current_server_id = None; log_view.show_logs_for_server(id, cx);
log_view.editor = None;
cx.notify(); cx.notify();
} }
}); });
@ -423,28 +581,31 @@ impl LspLogToolbarItemView {
fn show_logs_for_server(&mut self, id: LanguageServerId, cx: &mut ViewContext<Self>) { fn show_logs_for_server(&mut self, id: LanguageServerId, cx: &mut ViewContext<Self>) {
if let Some(log_view) = &self.log_view { if let Some(log_view) = &self.log_view {
log_view.update(cx, |log_view, cx| { log_view.update(cx, |view, cx| view.show_logs_for_server(id, cx));
log_view.show_logs_for_server(id, cx);
});
self.menu_open = false; self.menu_open = false;
cx.notify();
}
}
fn show_rpc_trace_for_server(&mut self, id: LanguageServerId, cx: &mut ViewContext<Self>) {
if let Some(log_view) = &self.log_view {
log_view.update(cx, |view, cx| view.show_rpc_trace_for_server(id, cx));
self.menu_open = false;
cx.notify();
} }
cx.notify();
} }
fn render_language_server_menu_header( fn render_language_server_menu_header(
current_server: Option<(LanguageServerId, LanguageServerName, WorktreeId, bool)>, current_server: Option<LogMenuItem>,
project: &ModelHandle<Project>,
theme: &Arc<Theme>, theme: &Arc<Theme>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> impl Element<Self> { ) -> impl Element<Self> {
enum ToggleMenu {} enum ToggleMenu {}
MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, cx| { MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, cx| {
let project = project.read(cx);
let label: Cow<str> = current_server let label: Cow<str> = current_server
.and_then(|(_, server_name, worktree_id, _)| { .and_then(|row| {
let worktree = project.worktree_for_id(worktree_id, cx)?; let worktree = row.worktree.read(cx);
let worktree = &worktree.read(cx); Some(format!("{} - ({})", row.server_name.0, worktree.root_name()).into())
Some(format!("{} - ({})", server_name.0, worktree.root_name()).into())
}) })
.unwrap_or_else(|| "No server selected".into()); .unwrap_or_else(|| "No server selected".into());
Label::new( Label::new(
@ -466,46 +627,67 @@ impl LspLogToolbarItemView {
fn render_language_server_menu_item( fn render_language_server_menu_item(
id: LanguageServerId, id: LanguageServerId,
name: LanguageServerName, name: LanguageServerName,
worktree_id: WorktreeId, worktree: ModelHandle<Worktree>,
logging_enabled: bool, logging_enabled: bool,
is_selected: bool, logs_selected: bool,
project: &ModelHandle<Project>, rpc_trace_selected: bool,
theme: &Arc<Theme>, theme: &Arc<Theme>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<impl Element<Self>> { ) -> impl Element<Self> {
enum ActivateLog {} enum ActivateLog {}
let project = project.read(cx); enum ActivateRpcTrace {}
let worktree = project.worktree_for_id(worktree_id, cx)?;
let worktree = &worktree.read(cx);
if !worktree.is_visible() {
return None;
}
let label = format!("{} - ({})", name.0, worktree.root_name());
Some( let header = format!("{} - ({})", name.0, worktree.read(cx).root_name());
MouseEventHandler::<ActivateLog, _>::new(id.0, cx, move |state, cx| {
let item_style = theme.context_menu.item.style_for(state, is_selected); let item_style = &theme.context_menu.item.default;
Flex::row() Flex::column()
.with_child(ui::checkbox_with_label::<Self, _, Self, _>( .with_child(
Empty::new(), Label::new(header, item_style.label.clone())
&theme.welcome.checkbox, .aligned()
logging_enabled, .left(),
id.0, )
cx, .with_child(
move |this, enabled, cx| { MouseEventHandler::<ActivateLog, _>::new(id.0, cx, move |state, _| {
this.toggle_logging_for_server(id, enabled, cx); let item_style = &theme.context_menu.item.style_for(state, logs_selected);
}, Label::new("logs", item_style.label.clone())
)) .aligned()
.with_child(Label::new(label, item_style.label.clone()).aligned().left()) .left()
.align_children_center() .contained()
.contained() .with_style(item_style.container)
.with_style(item_style.container) })
}) .with_cursor_style(CursorStyle::PointingHand)
.with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, view, cx| {
.on_click(MouseButton::Left, move |_, view, cx| { view.show_logs_for_server(id, cx);
view.show_logs_for_server(id, cx); }),
}), )
) .with_child(
MouseEventHandler::<ActivateRpcTrace, _>::new(id.0, cx, move |state, cx| {
let item_style = &theme.context_menu.item.style_for(state, rpc_trace_selected);
Flex::row()
.with_child(ui::checkbox_with_label::<Self, _, Self, _>(
Empty::new(),
&theme.welcome.checkbox,
logging_enabled,
id.0,
cx,
move |this, enabled, cx| {
this.toggle_logging_for_server(id, enabled, cx);
},
))
.with_child(
Label::new("rpc trace", item_style.label.clone())
.aligned()
.left(),
)
.align_children_center()
.contained()
.with_style(item_style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, view, cx| {
view.show_rpc_trace_for_server(id, cx);
}),
)
} }
} }

View file

@ -826,6 +826,11 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
let mut events = subscribe(&project, cx); let mut events = subscribe(&project, cx);
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
assert_eq!(
events.next().await.unwrap(),
Event::LanguageServerAdded(LanguageServerId(0)),
);
fake_server fake_server
.start_progress(format!("{}/0", progress_token)) .start_progress(format!("{}/0", progress_token))
.await; .await;
@ -953,6 +958,10 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
// Simulate the newly started server sending more diagnostics. // Simulate the newly started server sending more diagnostics.
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
assert_eq!(
events.next().await.unwrap(),
Event::LanguageServerAdded(LanguageServerId(1))
);
fake_server.start_progress(progress_token).await; fake_server.start_progress(progress_token).await;
assert_eq!( assert_eq!(
events.next().await.unwrap(), events.next().await.unwrap(),

View file

@ -3500,7 +3500,7 @@ impl std::fmt::Debug for OpenPaths {
} }
} }
pub struct WorkspaceCreated(WeakViewHandle<Workspace>); pub struct WorkspaceCreated(pub WeakViewHandle<Workspace>);
pub fn activate_workspace_for_project( pub fn activate_workspace_for_project(
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,

View file

@ -311,9 +311,8 @@ pub fn initialize_workspace(
toolbar.add_item(submit_feedback_button, cx); toolbar.add_item(submit_feedback_button, cx);
let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new()); let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
toolbar.add_item(feedback_info_text, cx); toolbar.add_item(feedback_info_text, cx);
let lsp_log_item = cx.add_view(|_| { let lsp_log_item =
lsp_log::LspLogToolbarItemView::new(workspace.project().clone()) cx.add_view(|_| lsp_log::LspLogToolbarItemView::new());
});
toolbar.add_item(lsp_log_item, cx); toolbar.add_item(lsp_log_item, cx);
}) })
}); });