ssh remoting: Forward LSP logs to client (#19212)

Release Notes:

- N/A

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
This commit is contained in:
Thorsten Ball 2024-10-15 16:04:29 +02:00 committed by GitHub
parent db7417f3b5
commit 397e4bee0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 245 additions and 95 deletions

View file

@ -838,6 +838,7 @@ impl Database {
.map(|language_server| proto::LanguageServer { .map(|language_server| proto::LanguageServer {
id: language_server.id as u64, id: language_server.id as u64,
name: language_server.name, name: language_server.name,
worktree_id: None,
}) })
.collect(), .collect(),
dev_server_project_id: project.dev_server_project_id, dev_server_project_id: project.dev_server_project_id,

View file

@ -718,6 +718,7 @@ impl Database {
.map(|language_server| proto::LanguageServer { .map(|language_server| proto::LanguageServer {
id: language_server.id as u64, id: language_server.id as u64,
name: language_server.name, name: language_server.name,
worktree_id: None,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -11,7 +11,7 @@ use language::{LanguageServerId, LanguageServerName};
use lsp::{ use lsp::{
notification::SetTrace, IoKind, LanguageServer, MessageType, SetTraceParams, TraceValue, notification::SetTrace, IoKind, LanguageServer, MessageType, SetTraceParams, TraceValue,
}; };
use project::{search::SearchQuery, Project}; use project::{search::SearchQuery, Project, WorktreeId};
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};
use workspace::{ use workspace::{
@ -99,6 +99,8 @@ impl Message for RpcMessage {
} }
struct LanguageServerState { struct LanguageServerState {
name: Option<LanguageServerName>,
worktree_id: Option<WorktreeId>,
kind: LanguageServerKind, kind: LanguageServerKind,
log_messages: VecDeque<LogMessage>, log_messages: VecDeque<LogMessage>,
trace_messages: VecDeque<TraceMessage>, trace_messages: VecDeque<TraceMessage>,
@ -108,15 +110,34 @@ struct LanguageServerState {
io_logs_subscription: Option<lsp::Subscription>, io_logs_subscription: Option<lsp::Subscription>,
} }
enum LanguageServerKind { #[derive(PartialEq, Clone)]
pub enum LanguageServerKind {
Local { project: WeakModel<Project> }, Local { project: WeakModel<Project> },
Global { name: LanguageServerName }, Remote { project: WeakModel<Project> },
Global,
}
impl LanguageServerKind {
fn is_remote(&self) -> bool {
matches!(self, LanguageServerKind::Remote { .. })
}
}
impl std::fmt::Debug for LanguageServerKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LanguageServerKind::Local { .. } => write!(f, "LanguageServerKind::Local"),
LanguageServerKind::Remote { .. } => write!(f, "LanguageServerKind::Remote"),
LanguageServerKind::Global => write!(f, "LanguageServerKind::Global"),
}
}
} }
impl LanguageServerKind { impl LanguageServerKind {
fn project(&self) -> Option<&WeakModel<Project>> { fn project(&self) -> Option<&WeakModel<Project>> {
match self { match self {
Self::Local { project } => Some(project), Self::Local { project } => Some(project),
Self::Remote { project } => Some(project),
Self::Global { .. } => None, Self::Global { .. } => None,
} }
} }
@ -175,6 +196,7 @@ pub(crate) struct LogMenuItem {
pub rpc_trace_enabled: bool, pub rpc_trace_enabled: bool,
pub selected_entry: LogKind, pub selected_entry: LogKind,
pub trace_level: lsp::TraceValue, pub trace_level: lsp::TraceValue,
pub server_kind: LanguageServerKind,
} }
actions!(debug, [OpenLanguageServerLogs]); actions!(debug, [OpenLanguageServerLogs]);
@ -184,7 +206,7 @@ pub fn init(cx: &mut AppContext) {
cx.observe_new_views(move |workspace: &mut Workspace, cx| { cx.observe_new_views(move |workspace: &mut Workspace, cx| {
let project = workspace.project(); let project = workspace.project();
if project.read(cx).is_local() { if project.read(cx).is_local() || project.read(cx).is_via_ssh() {
log_store.update(cx, |store, cx| { log_store.update(cx, |store, cx| {
store.add_project(project, cx); store.add_project(project, cx);
}); });
@ -193,7 +215,7 @@ pub fn init(cx: &mut AppContext) {
let log_store = log_store.clone(); let log_store = log_store.clone();
workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, cx| { workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, cx| {
let project = workspace.project().read(cx); let project = workspace.project().read(cx);
if project.is_local() { if project.is_local() || project.is_via_ssh() {
workspace.split_item( workspace.split_item(
SplitDirection::Right, SplitDirection::Right,
Box::new(cx.new_view(|cx| { Box::new(cx.new_view(|cx| {
@ -233,11 +255,12 @@ impl LogStore {
.ok(); .ok();
}, },
)); ));
let name = LanguageServerName::new_static("copilot");
this.add_language_server( this.add_language_server(
LanguageServerKind::Global { LanguageServerKind::Global,
name: LanguageServerName::new_static("copilot"),
},
server.server_id(), server.server_id(),
Some(name),
None,
Some(server.clone()), Some(server.clone()),
cx, cx,
); );
@ -279,42 +302,44 @@ impl LogStore {
this.language_servers this.language_servers
.retain(|_, state| state.kind.project() != Some(&weak_project)); .retain(|_, state| state.kind.project() != Some(&weak_project));
}), }),
cx.subscribe(project, |this, project, event, cx| match event { cx.subscribe(project, |this, project, event, cx| {
project::Event::LanguageServerAdded(id) => { let server_kind = if project.read(cx).is_via_ssh() {
let read_project = project.read(cx); LanguageServerKind::Remote {
if let Some(server) = read_project.language_server_for_id(*id, cx) { project: project.downgrade(),
}
} else {
LanguageServerKind::Local {
project: project.downgrade(),
}
};
match event {
project::Event::LanguageServerAdded(id, name, worktree_id) => {
this.add_language_server( this.add_language_server(
LanguageServerKind::Local { server_kind,
project: project.downgrade(), *id,
}, Some(name.clone()),
server.server_id(), *worktree_id,
Some(server), project.read(cx).language_server_for_id(*id, cx),
cx, cx,
); );
} }
} project::Event::LanguageServerRemoved(id) => {
project::Event::LanguageServerRemoved(id) => { this.remove_language_server(*id, cx);
this.remove_language_server(*id, cx); }
} project::Event::LanguageServerLog(id, typ, message) => {
project::Event::LanguageServerLog(id, typ, message) => { this.add_language_server(server_kind, *id, None, None, None, cx);
this.add_language_server( match typ {
LanguageServerKind::Local { project::LanguageServerLogType::Log(typ) => {
project: project.downgrade(), this.add_language_server_log(*id, *typ, message, cx);
}, }
*id, project::LanguageServerLogType::Trace(_) => {
None, this.add_language_server_trace(*id, message, cx);
cx, }
);
match typ {
project::LanguageServerLogType::Log(typ) => {
this.add_language_server_log(*id, *typ, message, cx);
}
project::LanguageServerLogType::Trace(_) => {
this.add_language_server_trace(*id, message, cx);
} }
} }
_ => {}
} }
_ => {}
}), }),
], ],
}, },
@ -332,12 +357,16 @@ impl LogStore {
&mut self, &mut self,
kind: LanguageServerKind, kind: LanguageServerKind,
server_id: LanguageServerId, server_id: LanguageServerId,
name: Option<LanguageServerName>,
worktree_id: Option<WorktreeId>,
server: Option<Arc<LanguageServer>>, server: Option<Arc<LanguageServer>>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Option<&mut LanguageServerState> { ) -> Option<&mut LanguageServerState> {
let server_state = self.language_servers.entry(server_id).or_insert_with(|| { let server_state = self.language_servers.entry(server_id).or_insert_with(|| {
cx.notify(); cx.notify();
LanguageServerState { LanguageServerState {
name: None,
worktree_id: None,
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),
@ -348,6 +377,13 @@ impl LogStore {
} }
}); });
if let Some(name) = name {
server_state.name = Some(name);
}
if let Some(worktree_id) = worktree_id {
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.filter(|_| server_state.io_logs_subscription.is_none()) {
let io_tx = self.io_tx.clone(); let io_tx = self.io_tx.clone();
let server_id = server.server_id(); let server_id = server.server_id();
@ -448,14 +484,14 @@ impl LogStore {
self.language_servers self.language_servers
.iter() .iter()
.filter_map(move |(id, state)| match &state.kind { .filter_map(move |(id, state)| match &state.kind {
LanguageServerKind::Local { project } => { LanguageServerKind::Local { project } | LanguageServerKind::Remote { project } => {
if project == lookup_project { if project == lookup_project {
Some(*id) Some(*id)
} else { } else {
None None
} }
} }
LanguageServerKind::Global { .. } => Some(*id), LanguageServerKind::Global => Some(*id),
}) })
} }
@ -662,21 +698,40 @@ impl LspLogView {
pub(crate) fn menu_items<'a>(&'a self, cx: &'a AppContext) -> Option<Vec<LogMenuItem>> { pub(crate) fn menu_items<'a>(&'a self, cx: &'a AppContext) -> Option<Vec<LogMenuItem>> {
let log_store = self.log_store.read(cx); let log_store = self.log_store.read(cx);
let mut rows = self let unknown_server = LanguageServerName::new_static("unknown server");
.project
.read(cx) let mut rows = log_store
.language_servers(cx) .language_servers
.filter_map(|(server_id, language_server_name, worktree_id)| { .iter()
let worktree = self.project.read(cx).worktree_for_id(worktree_id, cx)?; .filter_map(|(server_id, state)| match &state.kind {
let state = log_store.language_servers.get(&server_id)?; LanguageServerKind::Local { .. } | LanguageServerKind::Remote { .. } => {
Some(LogMenuItem { let worktree_root_name = state
server_id, .worktree_id
server_name: language_server_name, .and_then(|id| self.project.read(cx).worktree_for_id(id, cx))
worktree_root_name: worktree.read(cx).root_name().to_string(), .map(|worktree| worktree.read(cx).root_name().to_string())
.unwrap_or_else(|| "Unknown worktree".to_string());
let state = log_store.language_servers.get(&server_id)?;
Some(LogMenuItem {
server_id: *server_id,
server_name: state.name.clone().unwrap_or(unknown_server.clone()),
server_kind: state.kind.clone(),
worktree_root_name,
rpc_trace_enabled: state.rpc_state.is_some(),
selected_entry: self.active_entry_kind,
trace_level: lsp::TraceValue::Off,
})
}
LanguageServerKind::Global => Some(LogMenuItem {
server_id: *server_id,
server_name: state.name.clone().unwrap_or(unknown_server.clone()),
server_kind: state.kind.clone(),
worktree_root_name: "supplementary".to_string(),
rpc_trace_enabled: state.rpc_state.is_some(), rpc_trace_enabled: state.rpc_state.is_some(),
selected_entry: self.active_entry_kind, selected_entry: self.active_entry_kind,
trace_level: lsp::TraceValue::Off, trace_level: lsp::TraceValue::Off,
}) }),
}) })
.chain( .chain(
self.project self.project
@ -687,6 +742,7 @@ impl LspLogView {
Some(LogMenuItem { Some(LogMenuItem {
server_id, server_id,
server_name: name.clone(), server_name: name.clone(),
server_kind: state.kind.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(),
selected_entry: self.active_entry_kind, selected_entry: self.active_entry_kind,
@ -694,22 +750,6 @@ impl LspLogView {
}) })
}), }),
) )
.chain(
log_store
.language_servers
.iter()
.filter_map(|(server_id, state)| match &state.kind {
LanguageServerKind::Global { name } => Some(LogMenuItem {
server_id: *server_id,
server_name: name.clone(),
worktree_root_name: "supplementary".to_string(),
rpc_trace_enabled: state.rpc_state.is_some(),
selected_entry: self.active_entry_kind,
trace_level: lsp::TraceValue::Off,
}),
_ => None,
}),
)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
rows.sort_by_key(|row| row.server_id); rows.sort_by_key(|row| row.server_id);
rows.dedup_by_key(|row| row.server_id); rows.dedup_by_key(|row| row.server_id);
@ -1075,13 +1115,9 @@ 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.selected_entry == LogKind::Logs { // We do not support tracing for remote language servers right now
let selected_ix = menu.select_last(); if row.server_kind.is_remote() {
debug_assert_eq!( return menu;
Some(ix * 4 + 1),
selected_ix,
"Could not scroll to a just added LSP menu item"
);
} }
menu = menu.entry( menu = menu.entry(
SERVER_TRACE, SERVER_TRACE,
@ -1090,14 +1126,6 @@ impl Render for LspLogToolbarItemView {
view.show_trace_for_server(row.server_id, 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,
"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();

View file

@ -95,6 +95,9 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
rpc_trace_enabled: false, rpc_trace_enabled: false,
selected_entry: LogKind::Logs, selected_entry: LogKind::Logs,
trace_level: lsp::TraceValue::Off, trace_level: lsp::TraceValue::Off,
server_kind: lsp_log::LanguageServerKind::Local {
project: project.downgrade()
}
}] }]
); );
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");

View file

@ -696,7 +696,7 @@ pub struct LspStore {
} }
pub enum LspStoreEvent { pub enum LspStoreEvent {
LanguageServerAdded(LanguageServerId), LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
LanguageServerRemoved(LanguageServerId), LanguageServerRemoved(LanguageServerId),
LanguageServerUpdate { LanguageServerUpdate {
language_server_id: LanguageServerId, language_server_id: LanguageServerId,
@ -752,6 +752,7 @@ impl LspStore {
client.add_model_request_handler(Self::handle_restart_language_servers); client.add_model_request_handler(Self::handle_restart_language_servers);
client.add_model_message_handler(Self::handle_start_language_server); client.add_model_message_handler(Self::handle_start_language_server);
client.add_model_message_handler(Self::handle_update_language_server); client.add_model_message_handler(Self::handle_update_language_server);
client.add_model_message_handler(Self::handle_language_server_log);
client.add_model_message_handler(Self::handle_update_diagnostic_summary); client.add_model_message_handler(Self::handle_update_diagnostic_summary);
client.add_model_request_handler(Self::handle_format_buffers); client.add_model_request_handler(Self::handle_format_buffers);
client.add_model_request_handler(Self::handle_resolve_completion_documentation); client.add_model_request_handler(Self::handle_resolve_completion_documentation);
@ -3087,6 +3088,7 @@ impl LspStore {
server: Some(proto::LanguageServer { server: Some(proto::LanguageServer {
id: server_id.0 as u64, id: server_id.0 as u64,
name: status.name.clone(), name: status.name.clone(),
worktree_id: None,
}), }),
}) })
.log_err(); .log_err();
@ -3907,16 +3909,23 @@ impl LspStore {
.payload .payload
.server .server
.ok_or_else(|| anyhow!("invalid server"))?; .ok_or_else(|| anyhow!("invalid server"))?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
let server_id = LanguageServerId(server.id as usize);
this.language_server_statuses.insert( this.language_server_statuses.insert(
LanguageServerId(server.id as usize), server_id,
LanguageServerStatus { LanguageServerStatus {
name: server.name, name: server.name.clone(),
pending_work: Default::default(), pending_work: Default::default(),
has_pending_diagnostic_updates: false, has_pending_diagnostic_updates: false,
progress_tokens: Default::default(), progress_tokens: Default::default(),
}, },
); );
cx.emit(LspStoreEvent::LanguageServerAdded(
server_id,
LanguageServerName(server.name.into()),
server.worktree_id.map(WorktreeId::from_proto),
));
cx.notify(); cx.notify();
})?; })?;
Ok(()) Ok(())
@ -3984,6 +3993,29 @@ impl LspStore {
})? })?
} }
async fn handle_language_server_log(
this: Model<Self>,
envelope: TypedEnvelope<proto::LanguageServerLog>,
mut cx: AsyncAppContext,
) -> Result<()> {
let language_server_id = LanguageServerId(envelope.payload.language_server_id as usize);
let log_type = envelope
.payload
.log_type
.map(LanguageServerLogType::from_proto)
.context("invalid language server log type")?;
let message = envelope.payload.message;
this.update(&mut cx, |_, cx| {
cx.emit(LspStoreEvent::LanguageServerLog(
language_server_id,
log_type,
message,
));
})
}
pub fn disk_based_diagnostics_started( pub fn disk_based_diagnostics_started(
&mut self, &mut self,
language_server_id: LanguageServerId, language_server_id: LanguageServerId,
@ -6356,7 +6388,11 @@ impl LspStore {
}, },
); );
cx.emit(LspStoreEvent::LanguageServerAdded(server_id)); cx.emit(LspStoreEvent::LanguageServerAdded(
server_id,
language_server.name().into(),
Some(key.0),
));
if let Some((downstream_client, project_id)) = self.downstream_client.as_ref() { if let Some((downstream_client, project_id)) = self.downstream_client.as_ref() {
downstream_client downstream_client
@ -6365,6 +6401,7 @@ impl LspStore {
server: Some(proto::LanguageServer { server: Some(proto::LanguageServer {
id: server_id.0 as u64, id: server_id.0 as u64,
name: language_server.name().to_string(), name: language_server.name().to_string(),
worktree_id: Some(key.0.to_proto()),
}), }),
}) })
.log_err(); .log_err();
@ -6546,8 +6583,8 @@ impl LspStore {
if let Some(local) = self.as_local_mut() { if let Some(local) = self.as_local_mut() {
local local
.supplementary_language_servers .supplementary_language_servers
.insert(id, (name, server)); .insert(id, (name.clone(), server));
cx.emit(LspStoreEvent::LanguageServerAdded(id)); cx.emit(LspStoreEvent::LanguageServerAdded(id, name, None));
} }
} }
@ -7289,6 +7326,46 @@ pub enum LanguageServerLogType {
Trace(Option<String>), Trace(Option<String>),
} }
impl LanguageServerLogType {
pub fn to_proto(&self) -> proto::language_server_log::LogType {
match self {
Self::Log(log_type) => {
let message_type = match *log_type {
MessageType::ERROR => 1,
MessageType::WARNING => 2,
MessageType::INFO => 3,
MessageType::LOG => 4,
other => {
log::warn!("Unknown lsp log message type: {:?}", other);
4
}
};
proto::language_server_log::LogType::LogMessageType(message_type)
}
Self::Trace(message) => {
proto::language_server_log::LogType::LogTrace(proto::LspLogTrace {
message: message.clone(),
})
}
}
}
pub fn from_proto(log_type: proto::language_server_log::LogType) -> Self {
match log_type {
proto::language_server_log::LogType::LogMessageType(message_type) => {
Self::Log(match message_type {
1 => MessageType::ERROR,
2 => MessageType::WARNING,
3 => MessageType::INFO,
4 => MessageType::LOG,
_ => MessageType::LOG,
})
}
proto::language_server_log::LogType::LogTrace(trace) => Self::Trace(trace.message),
}
}
}
pub enum LanguageServerState { pub enum LanguageServerState {
Starting(Task<Option<Arc<LanguageServer>>>), Starting(Task<Option<Arc<LanguageServer>>>),

View file

@ -219,7 +219,7 @@ enum ProjectClientState {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Event { pub enum Event {
LanguageServerAdded(LanguageServerId), LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),
LanguageServerRemoved(LanguageServerId), LanguageServerRemoved(LanguageServerId),
LanguageServerLog(LanguageServerId, LanguageServerLogType, String), LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
Notification(String), Notification(String),
@ -2090,9 +2090,9 @@ impl Project {
path: path.clone(), path: path.clone(),
language_server_id: *language_server_id, language_server_id: *language_server_id,
}), }),
LspStoreEvent::LanguageServerAdded(language_server_id) => { LspStoreEvent::LanguageServerAdded(language_server_id, name, worktree_id) => cx.emit(
cx.emit(Event::LanguageServerAdded(*language_server_id)) Event::LanguageServerAdded(*language_server_id, name.clone(), *worktree_id),
} ),
LspStoreEvent::LanguageServerRemoved(language_server_id) => { LspStoreEvent::LanguageServerRemoved(language_server_id) => {
cx.emit(Event::LanguageServerRemoved(*language_server_id)) cx.emit(Event::LanguageServerRemoved(*language_server_id))
} }

View file

@ -1185,7 +1185,11 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
assert_eq!( assert_eq!(
events.next().await.unwrap(), events.next().await.unwrap(),
Event::LanguageServerAdded(LanguageServerId(0)), Event::LanguageServerAdded(
LanguageServerId(0),
fake_server.server.name().into(),
Some(worktree_id)
),
); );
fake_server fake_server
@ -1295,6 +1299,8 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
}, },
); );
let worktree_id = project.update(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
let buffer = project let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
.await .await
@ -1314,7 +1320,11 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
let fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
assert_eq!( assert_eq!(
events.next().await.unwrap(), events.next().await.unwrap(),
Event::LanguageServerAdded(LanguageServerId(1)) Event::LanguageServerAdded(
LanguageServerId(1),
fake_server.server.name().into(),
Some(worktree_id)
)
); );
fake_server.start_progress(progress_token).await; fake_server.start_progress(progress_token).await;
assert_eq!( assert_eq!(

View file

@ -269,7 +269,7 @@ message Envelope {
GetLlmToken get_llm_token = 235; GetLlmToken get_llm_token = 235;
GetLlmTokenResponse get_llm_token_response = 236; GetLlmTokenResponse get_llm_token_response = 236;
RefreshLlmToken refresh_llm_token = 259; // current max RefreshLlmToken refresh_llm_token = 259;
LspExtSwitchSourceHeader lsp_ext_switch_source_header = 241; LspExtSwitchSourceHeader lsp_ext_switch_source_header = 241;
LspExtSwitchSourceHeaderResponse lsp_ext_switch_source_header_response = 242; LspExtSwitchSourceHeaderResponse lsp_ext_switch_source_header_response = 242;
@ -286,6 +286,8 @@ message Envelope {
ShutdownRemoteServer shutdown_remote_server = 257; ShutdownRemoteServer shutdown_remote_server = 257;
RemoveWorktree remove_worktree = 258; RemoveWorktree remove_worktree = 258;
LanguageServerLog language_server_log = 260; // current max
} }
reserved 87 to 88; reserved 87 to 88;
@ -1294,6 +1296,7 @@ message LamportTimestamp {
message LanguageServer { message LanguageServer {
uint64 id = 1; uint64 id = 1;
string name = 2; string name = 2;
optional uint64 worktree_id = 3;
} }
message StartLanguageServer { message StartLanguageServer {
@ -1347,6 +1350,20 @@ message LspDiskBasedDiagnosticsUpdating {}
message LspDiskBasedDiagnosticsUpdated {} message LspDiskBasedDiagnosticsUpdated {}
message LanguageServerLog {
uint64 project_id = 1;
uint64 language_server_id = 2;
oneof log_type {
uint32 log_message_type = 3;
LspLogTrace log_trace = 4;
}
string message = 5;
}
message LspLogTrace {
optional string message = 1;
}
message UpdateChannels { message UpdateChannels {
repeated Channel channels = 1; repeated Channel channels = 1;
repeated uint64 delete_channels = 4; repeated uint64 delete_channels = 4;

View file

@ -366,6 +366,7 @@ messages!(
(CheckFileExistsResponse, Background), (CheckFileExistsResponse, Background),
(ShutdownRemoteServer, Foreground), (ShutdownRemoteServer, Foreground),
(RemoveWorktree, Foreground), (RemoveWorktree, Foreground),
(LanguageServerLog, Foreground),
); );
request_messages!( request_messages!(
@ -562,6 +563,7 @@ entity_messages!(
LspExtSwitchSourceHeader, LspExtSwitchSourceHeader,
UpdateUserSettings, UpdateUserSettings,
CheckFileExists, CheckFileExists,
LanguageServerLog,
); );
entity_messages!( entity_messages!(

View file

@ -33,6 +33,7 @@ gpui.workspace = true
language.workspace = true language.workspace = true
languages.workspace = true languages.workspace = true
log.workspace = true log.workspace = true
lsp.workspace = true
node_runtime.workspace = true node_runtime.workspace = true
project.workspace = true project.workspace = true
remote.workspace = true remote.workspace = true

View file

@ -203,6 +203,16 @@ impl HeadlessProject {
}) })
.log_err(); .log_err();
} }
LspStoreEvent::LanguageServerLog(language_server_id, log_type, message) => {
self.session
.send(proto::LanguageServerLog {
project_id: SSH_PROJECT_ID,
language_server_id: language_server_id.to_proto(),
message: message.clone(),
log_type: Some(log_type.to_proto()),
})
.log_err();
}
_ => {} _ => {}
} }
} }