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:
parent
db7417f3b5
commit
397e4bee0a
11 changed files with 245 additions and 95 deletions
|
@ -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,
|
||||||
|
|
|
@ -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<_>>();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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>>>),
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue