@@ -9,7 +9,8 @@ use gpui::{
};
use language::{LanguageServerId, LanguageServerName};
use lsp::{
- notification::SetTrace, IoKind, LanguageServer, MessageType, SetTraceParams, TraceValue,
+ notification::SetTrace, IoKind, LanguageServer, MessageType, ServerCapabilities,
+ SetTraceParams, TraceValue,
};
use project::{search::SearchQuery, Project, WorktreeId};
use std::{borrow::Cow, sync::Arc};
@@ -107,6 +108,7 @@ struct LanguageServerState {
rpc_state: Option<LanguageServerRpcState>,
trace_level: TraceValue,
log_level: MessageType,
+ capabilities: ServerCapabilities,
io_logs_subscription: Option<lsp::Subscription>,
}
@@ -176,6 +178,7 @@ pub enum LogKind {
Trace,
#[default]
Logs,
+ Capabilities,
}
impl LogKind {
@@ -184,6 +187,7 @@ impl LogKind {
LogKind::Rpc => RPC_MESSAGES,
LogKind::Trace => SERVER_TRACE,
LogKind::Logs => SERVER_LOGS,
+ LogKind::Capabilities => SERVER_CAPABILITIES,
}
}
}
@@ -374,6 +378,7 @@ impl LogStore {
trace_level: TraceValue::Off,
log_level: MessageType::LOG,
io_logs_subscription: None,
+ capabilities: ServerCapabilities::default(),
}
});
@@ -384,7 +389,10 @@ impl LogStore {
server_state.worktree_id = Some(worktree_id);
}
- if let Some(server) = server.filter(|_| server_state.io_logs_subscription.is_none()) {
+ if let Some(server) = server
+ .clone()
+ .filter(|_| server_state.io_logs_subscription.is_none())
+ {
let io_tx = self.io_tx.clone();
let server_id = server.server_id();
server_state.io_logs_subscription = Some(server.on_io(move |io_kind, message| {
@@ -393,6 +401,11 @@ impl LogStore {
.ok();
}));
}
+
+ if let Some(server) = server {
+ server_state.capabilities = server.capabilities();
+ }
+
Some(server_state)
}
@@ -477,6 +490,10 @@ impl LogStore {
Some(&self.language_servers.get(&server_id)?.trace_messages)
}
+ fn server_capabilities(&self, server_id: LanguageServerId) -> Option<&ServerCapabilities> {
+ Some(&self.language_servers.get(&server_id)?.capabilities)
+ }
+
fn server_ids_for_project<'a>(
&'a self,
lookup_project: &'a WeakModel<Project>,
@@ -602,6 +619,9 @@ impl LspLogView {
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
LogKind::Trace => this.show_trace_for_server(server_id, cx),
LogKind::Logs => this.show_logs_for_server(server_id, cx),
+ LogKind::Capabilities => {
+ this.show_capabilities_for_server(server_id, cx)
+ }
}
} else {
this.current_server_id = None;
@@ -618,6 +638,7 @@ impl LspLogView {
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
LogKind::Trace => this.show_trace_for_server(server_id, cx),
LogKind::Logs => this.show_logs_for_server(server_id, cx),
+ LogKind::Capabilities => this.show_capabilities_for_server(server_id, cx),
}
}
@@ -695,6 +716,33 @@ impl LspLogView {
(editor, vec![editor_subscription, search_subscription])
}
+ fn editor_for_capabilities(
+ capabilities: ServerCapabilities,
+ cx: &mut ViewContext<Self>,
+ ) -> (View<Editor>, Vec<Subscription>) {
+ let editor = cx.new_view(|cx| {
+ let mut editor = Editor::multi_line(cx);
+ editor.set_text(serde_json::to_string_pretty(&capabilities).unwrap(), cx);
+ editor.move_to_end(&MoveToEnd, cx);
+ editor.set_read_only(true);
+ editor.set_show_inline_completions(Some(false), cx);
+ editor
+ });
+ let editor_subscription = cx.subscribe(
+ &editor,
+ |_, _, event: &EditorEvent, cx: &mut ViewContext<'_, LspLogView>| {
+ cx.emit(event.clone())
+ },
+ );
+ let search_subscription = cx.subscribe(
+ &editor,
+ |_, _, event: &SearchEvent, cx: &mut ViewContext<'_, LspLogView>| {
+ cx.emit(event.clone())
+ },
+ );
+ (editor, vec![editor_subscription, search_subscription])
+ }
+
pub(crate) fn menu_items<'a>(&'a self, cx: &'a AppContext) -> Option<Vec<LogMenuItem>> {
let log_store = self.log_store.read(cx);
@@ -881,6 +929,7 @@ impl LspLogView {
cx.notify();
}
}
+
fn update_trace_level(
&self,
server_id: LanguageServerId,
@@ -899,6 +948,25 @@ impl LspLogView {
.ok();
}
}
+
+ fn show_capabilities_for_server(
+ &mut self,
+ server_id: LanguageServerId,
+ cx: &mut ViewContext<Self>,
+ ) {
+ let capabilities = self.log_store.read(cx).server_capabilities(server_id);
+
+ if let Some(capabilities) = capabilities {
+ self.current_server_id = Some(server_id);
+ self.active_entry_kind = LogKind::Capabilities;
+ let (editor, editor_subscriptions) =
+ Self::editor_for_capabilities(capabilities.clone(), cx);
+ self.editor = editor;
+ self.editor_subscriptions = editor_subscriptions;
+ cx.notify();
+ }
+ cx.focus(&self.focus_handle);
+ }
}
fn log_filter<T: Message>(line: &T, cmp: <T as Message>::Level) -> Option<&str> {
@@ -967,6 +1035,7 @@ impl Item for LspLogView {
LogKind::Rpc => new_view.show_rpc_trace_for_server(server_id, cx),
LogKind::Trace => new_view.show_trace_for_server(server_id, cx),
LogKind::Logs => new_view.show_logs_for_server(server_id, cx),
+ LogKind::Capabilities => new_view.show_capabilities_for_server(server_id, cx),
}
}
new_view
@@ -1168,6 +1237,13 @@ impl Render for LspLogToolbarItemView {
view.show_rpc_trace_for_server(row.server_id, cx);
}),
);
+ menu = menu.entry(
+ SERVER_CAPABILITIES,
+ None,
+ cx.handler_for(&log_view, move |view, cx| {
+ view.show_capabilities_for_server(row.server_id, cx);
+ }),
+ );
if server_selected && row.selected_entry == LogKind::Rpc {
let selected_ix = menu.select_last();
debug_assert_eq!(
@@ -1317,6 +1393,7 @@ impl Render for LspLogToolbarItemView {
const RPC_MESSAGES: &str = "RPC Messages";
const SERVER_LOGS: &str = "Server Logs";
const SERVER_TRACE: &str = "Server Trace";
+const SERVER_CAPABILITIES: &str = "Server Capabilities";
impl Default for LspLogToolbarItemView {
fn default() -> Self {