lsp: Add server-side tracing support (#15230)
This PR adds another row to the LSP log view: Server traces  [Traces](https://docs.rs/lsp-types/latest/lsp_types/notification/enum.LogTrace.html) are intended for logging execution diagnostics, which is different from `LogMessage` that we currently support. When `Server trace` is selected, user can select the level of tracing (`off`/`messages`/`verbose`) to their liking. Release Notes: - Added support for language server tracing to the LSP log view.
This commit is contained in:
parent
bb188f673e
commit
a875dd153d
3 changed files with 301 additions and 139 deletions
|
@ -8,7 +8,7 @@ use gpui::{
|
||||||
ViewContext, VisualContext, WeakModel, WindowContext,
|
ViewContext, VisualContext, WeakModel, WindowContext,
|
||||||
};
|
};
|
||||||
use language::{LanguageServerId, LanguageServerName};
|
use language::{LanguageServerId, LanguageServerName};
|
||||||
use lsp::{IoKind, LanguageServer};
|
use lsp::{notification::SetTrace, IoKind, LanguageServer, SetTraceParams, TraceValue};
|
||||||
use project::{search::SearchQuery, Project};
|
use project::{search::SearchQuery, Project};
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
use ui::{prelude::*, Button, Checkbox, ContextMenu, Label, PopoverMenu, Selection};
|
use ui::{prelude::*, Button, Checkbox, ContextMenu, Label, PopoverMenu, Selection};
|
||||||
|
@ -37,9 +37,12 @@ struct ProjectState {
|
||||||
struct LanguageServerState {
|
struct LanguageServerState {
|
||||||
kind: LanguageServerKind,
|
kind: LanguageServerKind,
|
||||||
log_messages: VecDeque<String>,
|
log_messages: VecDeque<String>,
|
||||||
|
trace_messages: VecDeque<String>,
|
||||||
rpc_state: Option<LanguageServerRpcState>,
|
rpc_state: Option<LanguageServerRpcState>,
|
||||||
|
trace_level: TraceValue,
|
||||||
_io_logs_subscription: Option<lsp::Subscription>,
|
_io_logs_subscription: Option<lsp::Subscription>,
|
||||||
_lsp_logs_subscription: Option<lsp::Subscription>,
|
_lsp_logs_subscription: Option<lsp::Subscription>,
|
||||||
|
_lsp_trace_subscription: Option<lsp::Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LanguageServerKind {
|
enum LanguageServerKind {
|
||||||
|
@ -66,7 +69,7 @@ pub struct LspLogView {
|
||||||
editor_subscriptions: Vec<Subscription>,
|
editor_subscriptions: Vec<Subscription>,
|
||||||
log_store: Model<LogStore>,
|
log_store: Model<LogStore>,
|
||||||
current_server_id: Option<LanguageServerId>,
|
current_server_id: Option<LanguageServerId>,
|
||||||
is_showing_rpc_trace: bool,
|
active_entry_kind: LogKind,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
_log_store_subscriptions: Vec<Subscription>,
|
_log_store_subscriptions: Vec<Subscription>,
|
||||||
|
@ -83,14 +86,32 @@ enum MessageKind {
|
||||||
Receive,
|
Receive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub enum LogKind {
|
||||||
|
Rpc,
|
||||||
|
Trace,
|
||||||
|
#[default]
|
||||||
|
Logs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogKind {
|
||||||
|
fn label(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
LogKind::Rpc => RPC_MESSAGES,
|
||||||
|
LogKind::Trace => SERVER_TRACE,
|
||||||
|
LogKind::Logs => SERVER_LOGS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(crate) struct LogMenuItem {
|
pub(crate) struct LogMenuItem {
|
||||||
pub server_id: LanguageServerId,
|
pub server_id: LanguageServerId,
|
||||||
pub server_name: LanguageServerName,
|
pub server_name: LanguageServerName,
|
||||||
pub worktree_root_name: String,
|
pub worktree_root_name: String,
|
||||||
pub rpc_trace_enabled: bool,
|
pub rpc_trace_enabled: bool,
|
||||||
pub rpc_trace_selected: bool,
|
pub selected_entry: LogKind,
|
||||||
pub logs_selected: bool,
|
pub trace_level: lsp::TraceValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
actions!(debug, [OpenLanguageServerLogs]);
|
actions!(debug, [OpenLanguageServerLogs]);
|
||||||
|
@ -244,12 +265,17 @@ impl LogStore {
|
||||||
kind,
|
kind,
|
||||||
rpc_state: None,
|
rpc_state: None,
|
||||||
log_messages: VecDeque::with_capacity(MAX_STORED_LOG_ENTRIES),
|
log_messages: VecDeque::with_capacity(MAX_STORED_LOG_ENTRIES),
|
||||||
|
trace_messages: VecDeque::with_capacity(MAX_STORED_LOG_ENTRIES),
|
||||||
|
trace_level: TraceValue::Off,
|
||||||
_io_logs_subscription: None,
|
_io_logs_subscription: None,
|
||||||
_lsp_logs_subscription: None,
|
_lsp_logs_subscription: None,
|
||||||
|
_lsp_trace_subscription: None,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if server.has_notification_handler::<lsp::notification::LogMessage>() {
|
if server.has_notification_handler::<lsp::notification::LogMessage>()
|
||||||
|
|| server.has_notification_handler::<lsp::notification::LogTrace>()
|
||||||
|
{
|
||||||
// Another event wants to re-add the server that was already added and subscribed to, avoid doing it again.
|
// Another event wants to re-add the server that was already added and subscribed to, avoid doing it again.
|
||||||
return Some(server_state);
|
return Some(server_state);
|
||||||
}
|
}
|
||||||
|
@ -264,6 +290,7 @@ impl LogStore {
|
||||||
let this = cx.handle().downgrade();
|
let this = cx.handle().downgrade();
|
||||||
server_state._lsp_logs_subscription =
|
server_state._lsp_logs_subscription =
|
||||||
Some(server.on_notification::<lsp::notification::LogMessage, _>({
|
Some(server.on_notification::<lsp::notification::LogMessage, _>({
|
||||||
|
let this = this.clone();
|
||||||
move |params, mut cx| {
|
move |params, mut cx| {
|
||||||
if let Some(this) = this.upgrade() {
|
if let Some(this) = this.upgrade() {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
|
@ -273,6 +300,17 @@ impl LogStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
server_state._lsp_trace_subscription =
|
||||||
|
Some(server.on_notification::<lsp::notification::LogTrace, _>({
|
||||||
|
move |params, mut cx| {
|
||||||
|
if let Some(this) = this.upgrade() {
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.add_language_server_trace(server_id, ¶ms.message, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
Some(server_state)
|
Some(server_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +323,30 @@ impl LogStore {
|
||||||
let language_server_state = self.get_language_server_state(id)?;
|
let language_server_state = self.get_language_server_state(id)?;
|
||||||
|
|
||||||
let log_lines = &mut language_server_state.log_messages;
|
let log_lines = &mut language_server_state.log_messages;
|
||||||
|
Self::add_language_server_message(log_lines, id, message, LogKind::Logs, cx);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_language_server_trace(
|
||||||
|
&mut self,
|
||||||
|
id: LanguageServerId,
|
||||||
|
message: &str,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let language_server_state = self.get_language_server_state(id)?;
|
||||||
|
|
||||||
|
let log_lines = &mut language_server_state.trace_messages;
|
||||||
|
Self::add_language_server_message(log_lines, id, message, LogKind::Trace, cx);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_language_server_message(
|
||||||
|
log_lines: &mut VecDeque<String>,
|
||||||
|
id: LanguageServerId,
|
||||||
|
message: &str,
|
||||||
|
kind: LogKind,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
while log_lines.len() >= MAX_STORED_LOG_ENTRIES {
|
while log_lines.len() >= MAX_STORED_LOG_ENTRIES {
|
||||||
log_lines.pop_front();
|
log_lines.pop_front();
|
||||||
}
|
}
|
||||||
|
@ -293,10 +355,9 @@ impl LogStore {
|
||||||
cx.emit(Event::NewServerLogEntry {
|
cx.emit(Event::NewServerLogEntry {
|
||||||
id,
|
id,
|
||||||
entry: message.to_string(),
|
entry: message.to_string(),
|
||||||
is_rpc: false,
|
kind,
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
Some(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_language_server(&mut self, id: LanguageServerId, cx: &mut ModelContext<Self>) {
|
fn remove_language_server(&mut self, id: LanguageServerId, cx: &mut ModelContext<Self>) {
|
||||||
|
@ -307,7 +368,9 @@ impl LogStore {
|
||||||
fn server_logs(&self, server_id: LanguageServerId) -> Option<&VecDeque<String>> {
|
fn server_logs(&self, server_id: LanguageServerId) -> Option<&VecDeque<String>> {
|
||||||
Some(&self.language_servers.get(&server_id)?.log_messages)
|
Some(&self.language_servers.get(&server_id)?.log_messages)
|
||||||
}
|
}
|
||||||
|
fn server_trace(&self, server_id: LanguageServerId) -> Option<&VecDeque<String>> {
|
||||||
|
Some(&self.language_servers.get(&server_id)?.trace_messages)
|
||||||
|
}
|
||||||
fn server_ids_for_project<'a>(
|
fn server_ids_for_project<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
lookup_project: &'a WeakModel<Project>,
|
lookup_project: &'a WeakModel<Project>,
|
||||||
|
@ -386,7 +449,7 @@ impl LogStore {
|
||||||
cx.emit(Event::NewServerLogEntry {
|
cx.emit(Event::NewServerLogEntry {
|
||||||
id: language_server_id,
|
id: language_server_id,
|
||||||
entry: line_before_message.to_string(),
|
entry: line_before_message.to_string(),
|
||||||
is_rpc: true,
|
kind: LogKind::Rpc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +461,7 @@ impl LogStore {
|
||||||
cx.emit(Event::NewServerLogEntry {
|
cx.emit(Event::NewServerLogEntry {
|
||||||
id: language_server_id,
|
id: language_server_id,
|
||||||
entry: message.to_string(),
|
entry: message.to_string(),
|
||||||
is_rpc: true,
|
kind: LogKind::Rpc,
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
Some(())
|
Some(())
|
||||||
|
@ -425,10 +488,10 @@ impl LspLogView {
|
||||||
if let Some(current_lsp) = this.current_server_id {
|
if let Some(current_lsp) = this.current_server_id {
|
||||||
if !store.read(cx).language_servers.contains_key(¤t_lsp) {
|
if !store.read(cx).language_servers.contains_key(¤t_lsp) {
|
||||||
if let Some(server_id) = first_server_id_for_project {
|
if let Some(server_id) = first_server_id_for_project {
|
||||||
if this.is_showing_rpc_trace {
|
match this.active_entry_kind {
|
||||||
this.show_rpc_trace_for_server(server_id, cx)
|
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
||||||
} else {
|
LogKind::Trace => this.show_trace_for_server(server_id, cx),
|
||||||
this.show_logs_for_server(server_id, cx)
|
LogKind::Logs => this.show_logs_for_server(server_id, cx),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.current_server_id = None;
|
this.current_server_id = None;
|
||||||
|
@ -441,21 +504,19 @@ impl LspLogView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(server_id) = first_server_id_for_project {
|
} else if let Some(server_id) = first_server_id_for_project {
|
||||||
if this.is_showing_rpc_trace {
|
match this.active_entry_kind {
|
||||||
this.show_rpc_trace_for_server(server_id, cx)
|
LogKind::Rpc => this.show_rpc_trace_for_server(server_id, cx),
|
||||||
} else {
|
LogKind::Trace => this.show_trace_for_server(server_id, cx),
|
||||||
this.show_logs_for_server(server_id, cx)
|
LogKind::Logs => this.show_logs_for_server(server_id, cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
let events_subscriptions = cx.subscribe(&log_store, |log_view, _, e, cx| match e {
|
let events_subscriptions = cx.subscribe(&log_store, |log_view, _, e, cx| match e {
|
||||||
Event::NewServerLogEntry { id, entry, is_rpc } => {
|
Event::NewServerLogEntry { id, entry, kind } => {
|
||||||
if log_view.current_server_id == Some(*id) {
|
if log_view.current_server_id == Some(*id) {
|
||||||
if (*is_rpc && log_view.is_showing_rpc_trace)
|
if *kind == log_view.active_entry_kind {
|
||||||
|| (!*is_rpc && !log_view.is_showing_rpc_trace)
|
|
||||||
{
|
|
||||||
log_view.editor.update(cx, |editor, cx| {
|
log_view.editor.update(cx, |editor, cx| {
|
||||||
editor.set_read_only(false);
|
editor.set_read_only(false);
|
||||||
let last_point = editor.buffer().read(cx).len(cx);
|
let last_point = editor.buffer().read(cx).len(cx);
|
||||||
|
@ -486,7 +547,7 @@ impl LspLogView {
|
||||||
project,
|
project,
|
||||||
log_store,
|
log_store,
|
||||||
current_server_id: None,
|
current_server_id: None,
|
||||||
is_showing_rpc_trace: false,
|
active_entry_kind: LogKind::Logs,
|
||||||
_log_store_subscriptions: vec![
|
_log_store_subscriptions: vec![
|
||||||
model_changes_subscription,
|
model_changes_subscription,
|
||||||
events_subscriptions,
|
events_subscriptions,
|
||||||
|
@ -541,10 +602,8 @@ impl LspLogView {
|
||||||
server_name: language_server_name,
|
server_name: language_server_name,
|
||||||
worktree_root_name: worktree.read(cx).root_name().to_string(),
|
worktree_root_name: worktree.read(cx).root_name().to_string(),
|
||||||
rpc_trace_enabled: state.rpc_state.is_some(),
|
rpc_trace_enabled: state.rpc_state.is_some(),
|
||||||
rpc_trace_selected: self.is_showing_rpc_trace
|
selected_entry: self.active_entry_kind,
|
||||||
&& self.current_server_id == Some(server_id),
|
trace_level: lsp::TraceValue::Off,
|
||||||
logs_selected: !self.is_showing_rpc_trace
|
|
||||||
&& self.current_server_id == Some(server_id),
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.chain(
|
.chain(
|
||||||
|
@ -558,10 +617,8 @@ impl LspLogView {
|
||||||
server_name: name.clone(),
|
server_name: name.clone(),
|
||||||
worktree_root_name: "supplementary".to_string(),
|
worktree_root_name: "supplementary".to_string(),
|
||||||
rpc_trace_enabled: state.rpc_state.is_some(),
|
rpc_trace_enabled: state.rpc_state.is_some(),
|
||||||
rpc_trace_selected: self.is_showing_rpc_trace
|
selected_entry: self.active_entry_kind,
|
||||||
&& self.current_server_id == Some(server_id),
|
trace_level: lsp::TraceValue::Off,
|
||||||
logs_selected: !self.is_showing_rpc_trace
|
|
||||||
&& self.current_server_id == Some(server_id),
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -575,10 +632,8 @@ impl LspLogView {
|
||||||
server_name: name.clone(),
|
server_name: name.clone(),
|
||||||
worktree_root_name: "supplementary".to_string(),
|
worktree_root_name: "supplementary".to_string(),
|
||||||
rpc_trace_enabled: state.rpc_state.is_some(),
|
rpc_trace_enabled: state.rpc_state.is_some(),
|
||||||
rpc_trace_selected: self.is_showing_rpc_trace
|
selected_entry: self.active_entry_kind,
|
||||||
&& self.current_server_id == Some(*server_id),
|
trace_level: lsp::TraceValue::Off,
|
||||||
logs_selected: !self.is_showing_rpc_trace
|
|
||||||
&& self.current_server_id == Some(*server_id),
|
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
|
@ -597,7 +652,23 @@ impl LspLogView {
|
||||||
.map(log_contents);
|
.map(log_contents);
|
||||||
if let Some(log_contents) = log_contents {
|
if let Some(log_contents) = log_contents {
|
||||||
self.current_server_id = Some(server_id);
|
self.current_server_id = Some(server_id);
|
||||||
self.is_showing_rpc_trace = false;
|
self.active_entry_kind = LogKind::Logs;
|
||||||
|
let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, cx);
|
||||||
|
self.editor = editor;
|
||||||
|
self.editor_subscriptions = editor_subscriptions;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
cx.focus(&self.focus_handle);
|
||||||
|
}
|
||||||
|
fn show_trace_for_server(&mut self, server_id: LanguageServerId, cx: &mut ViewContext<Self>) {
|
||||||
|
let log_contents = self
|
||||||
|
.log_store
|
||||||
|
.read(cx)
|
||||||
|
.server_trace(server_id)
|
||||||
|
.map(log_contents);
|
||||||
|
if let Some(log_contents) = log_contents {
|
||||||
|
self.current_server_id = Some(server_id);
|
||||||
|
self.active_entry_kind = LogKind::Trace;
|
||||||
let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, cx);
|
let (editor, editor_subscriptions) = Self::editor_for_logs(log_contents, cx);
|
||||||
self.editor = editor;
|
self.editor = editor;
|
||||||
self.editor_subscriptions = editor_subscriptions;
|
self.editor_subscriptions = editor_subscriptions;
|
||||||
|
@ -618,7 +689,7 @@ impl LspLogView {
|
||||||
});
|
});
|
||||||
if let Some(rpc_log) = rpc_log {
|
if let Some(rpc_log) = rpc_log {
|
||||||
self.current_server_id = Some(server_id);
|
self.current_server_id = Some(server_id);
|
||||||
self.is_showing_rpc_trace = true;
|
self.active_entry_kind = LogKind::Rpc;
|
||||||
let (editor, editor_subscriptions) = Self::editor_for_logs(rpc_log, cx);
|
let (editor, editor_subscriptions) = Self::editor_for_logs(rpc_log, cx);
|
||||||
let language = self.project.read(cx).languages().language_for_name("JSON");
|
let language = self.project.read(cx).languages().language_for_name("JSON");
|
||||||
editor
|
editor
|
||||||
|
@ -666,6 +737,24 @@ impl LspLogView {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn update_trace_level(
|
||||||
|
&self,
|
||||||
|
server_id: LanguageServerId,
|
||||||
|
level: TraceValue,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
if let Some(server) = self.project.read(cx).language_server_for_id(server_id) {
|
||||||
|
self.log_store.update(cx, |this, _| {
|
||||||
|
if let Some(state) = this.get_language_server_state(server_id) {
|
||||||
|
state.trace_level = level;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server
|
||||||
|
.notify::<SetTrace>(SetTraceParams { value: level })
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_contents(lines: &VecDeque<String>) -> String {
|
fn log_contents(lines: &VecDeque<String>) -> String {
|
||||||
|
@ -826,16 +915,14 @@ impl Render for LspLogToolbarItemView {
|
||||||
"{} ({}) - {}",
|
"{} ({}) - {}",
|
||||||
row.server_name.0,
|
row.server_name.0,
|
||||||
row.worktree_root_name,
|
row.worktree_root_name,
|
||||||
if row.rpc_trace_selected {
|
row.selected_entry.label()
|
||||||
RPC_MESSAGES
|
|
||||||
} else {
|
|
||||||
SERVER_LOGS
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "No server selected".into()),
|
.unwrap_or_else(|| "No server selected".into()),
|
||||||
))
|
))
|
||||||
.menu(move |cx| {
|
.menu({
|
||||||
|
let log_view = log_view.clone();
|
||||||
|
move |cx| {
|
||||||
let menu_rows = menu_rows.clone();
|
let menu_rows = menu_rows.clone();
|
||||||
let log_view = log_view.clone();
|
let log_view = log_view.clone();
|
||||||
let log_toolbar_view = log_toolbar_view.clone();
|
let log_toolbar_view = log_toolbar_view.clone();
|
||||||
|
@ -854,15 +941,29 @@ impl Render for LspLogToolbarItemView {
|
||||||
view.show_logs_for_server(row.server_id, cx);
|
view.show_logs_for_server(row.server_id, cx);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if server_selected && row.logs_selected {
|
if server_selected && row.selected_entry == LogKind::Logs {
|
||||||
let selected_ix = menu.select_last();
|
let selected_ix = menu.select_last();
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
Some(ix * 3 + 1),
|
Some(ix * 4 + 1),
|
||||||
|
selected_ix,
|
||||||
|
"Could not scroll to a just added LSP menu item"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
menu = menu.entry(
|
||||||
|
SERVER_TRACE,
|
||||||
|
None,
|
||||||
|
cx.handler_for(&log_view, move |view, cx| {
|
||||||
|
view.show_trace_for_server(row.server_id, cx);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if server_selected && row.selected_entry == LogKind::Trace {
|
||||||
|
let selected_ix = menu.select_last();
|
||||||
|
debug_assert_eq!(
|
||||||
|
Some(ix * 4 + 2),
|
||||||
selected_ix,
|
selected_ix,
|
||||||
"Could not scroll to a just added LSP menu item"
|
"Could not scroll to a just added LSP menu item"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu = menu.custom_entry(
|
menu = menu.custom_entry(
|
||||||
{
|
{
|
||||||
let log_toolbar_view = log_toolbar_view.clone();
|
let log_toolbar_view = log_toolbar_view.clone();
|
||||||
|
@ -905,10 +1006,10 @@ impl Render for LspLogToolbarItemView {
|
||||||
view.show_rpc_trace_for_server(row.server_id, cx);
|
view.show_rpc_trace_for_server(row.server_id, cx);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if server_selected && row.rpc_trace_selected {
|
if server_selected && row.selected_entry == LogKind::Rpc {
|
||||||
let selected_ix = menu.select_last();
|
let selected_ix = menu.select_last();
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
Some(ix * 3 + 2),
|
Some(ix * 4 + 3),
|
||||||
selected_ix,
|
selected_ix,
|
||||||
"Could not scroll to a just added LSP menu item"
|
"Could not scroll to a just added LSP menu item"
|
||||||
);
|
);
|
||||||
|
@ -917,9 +1018,13 @@ impl Render for LspLogToolbarItemView {
|
||||||
menu
|
menu
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
h_flex().size_full().child(lsp_menu).child(
|
h_flex()
|
||||||
|
.size_full()
|
||||||
|
.child(lsp_menu)
|
||||||
|
.child(
|
||||||
div()
|
div()
|
||||||
.child(
|
.child(
|
||||||
Button::new("clear_log_button", "Clear").on_click(cx.listener(
|
Button::new("clear_log_button", "Clear").on_click(cx.listener(
|
||||||
|
@ -938,11 +1043,67 @@ impl Render for LspLogToolbarItemView {
|
||||||
)
|
)
|
||||||
.ml_2(),
|
.ml_2(),
|
||||||
)
|
)
|
||||||
|
.child(log_view.update(cx, |this, _| {
|
||||||
|
if this.active_entry_kind == LogKind::Trace {
|
||||||
|
let log_view = log_view.clone();
|
||||||
|
div().child(
|
||||||
|
PopoverMenu::new("lsp-trace-level-menu")
|
||||||
|
.anchor(AnchorCorner::TopLeft)
|
||||||
|
.trigger(Button::new(
|
||||||
|
"language_server_trace_level_selector",
|
||||||
|
"Trace level",
|
||||||
|
))
|
||||||
|
.menu({
|
||||||
|
let log_view = log_view.clone();
|
||||||
|
|
||||||
|
move |cx| {
|
||||||
|
let id = log_view.read(cx).current_server_id?;
|
||||||
|
|
||||||
|
let trace_level = log_view.update(cx, |this, cx| {
|
||||||
|
this.log_store.update(cx, |this, _| {
|
||||||
|
Some(this.get_language_server_state(id)?.trace_level)
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
ContextMenu::build(cx, |mut menu, _| {
|
||||||
|
let log_view = log_view.clone();
|
||||||
|
|
||||||
|
for (option, label) in [
|
||||||
|
(TraceValue::Off, "Off"),
|
||||||
|
(TraceValue::Messages, "Messages"),
|
||||||
|
(TraceValue::Verbose, "Verbose"),
|
||||||
|
] {
|
||||||
|
menu = menu.entry(label, None, {
|
||||||
|
let log_view = log_view.clone();
|
||||||
|
move |cx| {
|
||||||
|
log_view.update(cx, |this, cx| {
|
||||||
|
if let Some(id) = this.current_server_id {
|
||||||
|
this.update_trace_level(id, option, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if option == trace_level {
|
||||||
|
menu.select_last();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
div()
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RPC_MESSAGES: &str = "RPC Messages";
|
const RPC_MESSAGES: &str = "RPC Messages";
|
||||||
const SERVER_LOGS: &str = "Server Logs";
|
const SERVER_LOGS: &str = "Server Logs";
|
||||||
|
const SERVER_TRACE: &str = "Server Trace";
|
||||||
|
|
||||||
impl LspLogToolbarItemView {
|
impl LspLogToolbarItemView {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
@ -979,7 +1140,7 @@ pub enum Event {
|
||||||
NewServerLogEntry {
|
NewServerLogEntry {
|
||||||
id: LanguageServerId,
|
id: LanguageServerId,
|
||||||
entry: String,
|
entry: String,
|
||||||
is_rpc: bool,
|
kind: LogKind,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use gpui::{Context, SemanticVersion, TestAppContext, VisualTestContext};
|
||||||
use language::{
|
use language::{
|
||||||
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageServerName,
|
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageServerName,
|
||||||
};
|
};
|
||||||
|
use lsp_log::LogKind;
|
||||||
use project::{FakeFs, Project};
|
use project::{FakeFs, Project};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
|
@ -92,8 +93,8 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
|
||||||
.root_name()
|
.root_name()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
rpc_trace_enabled: false,
|
rpc_trace_enabled: false,
|
||||||
rpc_trace_selected: false,
|
selected_entry: LogKind::Logs,
|
||||||
logs_selected: true,
|
trace_level: lsp::TraceValue::Off,
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(view.editor.read(cx).text(cx), "hello from the server\n");
|
assert_eq!(view.editor.read(cx).text(cx), "hello from the server\n");
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl FluentBuilder for ContextMenu {}
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
pub fn build(
|
pub fn build(
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
f: impl FnOnce(Self, &mut WindowContext) -> Self,
|
f: impl FnOnce(Self, &mut ViewContext<Self>) -> Self,
|
||||||
) -> View<Self> {
|
) -> View<Self> {
|
||||||
cx.new_view(|cx| {
|
cx.new_view(|cx| {
|
||||||
let focus_handle = cx.focus_handle();
|
let focus_handle = cx.focus_handle();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue