From 91fac2aa76578da874338e7097c0383b27cb7465 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 19 Sep 2023 11:47:27 +0300 Subject: [PATCH 1/8] Automatically subscribe for lsp logs of every server added Avoid re-adding the server on new logs events. --- crates/language_tools/src/lsp_log.rs | 40 +++++++++++++++++++++------- crates/project/src/project.rs | 12 --------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index 587e6ed25a..95b1bbd55f 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -38,7 +38,8 @@ struct ProjectState { struct LanguageServerState { log_buffer: ModelHandle, rpc_state: Option, - _subscription: Option, + _io_logs_subscription: Option, + _lsp_logs_subscription: Option, } struct LanguageServerRpcState { @@ -134,8 +135,6 @@ impl LogStore { } pub fn add_project(&mut self, project: &ModelHandle, cx: &mut ModelContext) { - use project::Event::*; - let weak_project = project.downgrade(); self.projects.insert( weak_project, @@ -146,13 +145,13 @@ impl LogStore { this.projects.remove(&weak_project); }), cx.subscribe(project, |this, project, event, cx| match event { - LanguageServerAdded(id) => { + project::Event::LanguageServerAdded(id) => { this.add_language_server(&project, *id, cx); } - LanguageServerRemoved(id) => { + project::Event::LanguageServerRemoved(id) => { this.remove_language_server(&project, *id, cx); } - LanguageServerLog(id, message) => { + project::Event::LanguageServerLog(id, message) => { this.add_language_server_log(&project, *id, message, cx); } _ => {} @@ -176,20 +175,34 @@ impl LogStore { log_buffer: cx .add_model(|cx| Buffer::new(0, cx.model_id() as u64, "")) .clone(), - _subscription: None, + _io_logs_subscription: None, + _lsp_logs_subscription: None, } }); let server = project.read(cx).language_server_for_id(id); let weak_project = project.downgrade(); let io_tx = self.io_tx.clone(); - server_state._subscription = server.map(|server| { + server_state._io_logs_subscription = server.as_ref().map(|server| { server.on_io(move |io_kind, message| { io_tx .unbounded_send((weak_project, id, io_kind, message.to_string())) .ok(); }) }); + let weak_project = project.downgrade(); + server_state._lsp_logs_subscription = server.map(|server| { + let server_id = server.server_id(); + server.on_notification::({ + move |params, mut cx| { + if let Some(project) = weak_project.upgrade(&cx) { + project.update(&mut cx, |_, cx| { + cx.emit(project::Event::LanguageServerLog(server_id, params.message)) + }); + } + } + }) + }); Some(server_state.log_buffer.clone()) } @@ -201,7 +214,16 @@ impl LogStore { message: &str, cx: &mut ModelContext, ) -> Option<()> { - let buffer = self.add_language_server(&project, id, cx)?; + let buffer = match self + .projects + .get_mut(&project.downgrade())? + .servers + .get(&id) + .map(|state| state.log_buffer.clone()) + { + Some(existing_buffer) => existing_buffer, + None => self.add_language_server(&project, id, cx)?, + }; buffer.update(cx, |buffer, cx| { let len = buffer.len(); let has_newline = message.ends_with("\n"); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b4e698e08a..27f3e45a05 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2789,18 +2789,6 @@ impl Project { None => return Ok(None), }; - language_server - .on_notification::({ - move |params, mut cx| { - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |_, cx| { - cx.emit(Event::LanguageServerLog(server_id, params.message)) - }); - } - } - }) - .detach(); - language_server .on_notification::({ let adapter = adapter.clone(); From 5b0b2fe50b00f40a1bbcbe2c89151a787e0e3ef7 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 19 Sep 2023 12:32:02 +0300 Subject: [PATCH 2/8] Add a specific server id to a Copilot LSP --- crates/copilot/src/copilot.rs | 31 ++++++++++++++++++++----------- crates/language/src/language.rs | 4 ++++ crates/zed/src/main.rs | 3 ++- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 28c20d95bb..4551fe0358 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -41,10 +41,15 @@ actions!( [Suggest, NextSuggestion, PreviousSuggestion, Reinstall] ); -pub fn init(http: Arc, node_runtime: Arc, cx: &mut AppContext) { +pub fn init( + new_server_id: LanguageServerId, + http: Arc, + node_runtime: Arc, + cx: &mut AppContext, +) { let copilot = cx.add_model({ let node_runtime = node_runtime.clone(); - move |cx| Copilot::start(http, node_runtime, cx) + move |cx| Copilot::start(new_server_id, http, node_runtime, cx) }); cx.set_global(copilot.clone()); @@ -268,6 +273,7 @@ pub struct Copilot { node_runtime: Arc, server: CopilotServer, buffers: HashSet>, + server_id: LanguageServerId, } impl Entity for Copilot { @@ -298,11 +304,13 @@ impl Copilot { } fn start( + new_server_id: LanguageServerId, http: Arc, node_runtime: Arc, cx: &mut ModelContext, ) -> Self { let mut this = Self { + server_id: new_server_id, http, node_runtime, server: CopilotServer::Disabled, @@ -315,13 +323,16 @@ impl Copilot { } fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext) { + let server_id = self.server_id; let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); if all_language_settings(None, cx).copilot_enabled(None, None) { if matches!(self.server, CopilotServer::Disabled) { let start_task = cx .spawn({ - move |this, cx| Self::start_language_server(http, node_runtime, this, cx) + move |this, cx| { + Self::start_language_server(server_id, http, node_runtime, this, cx) + } }) .shared(); self.server = CopilotServer::Starting { task: start_task }; @@ -342,6 +353,7 @@ impl Copilot { let http = util::http::FakeHttpClient::create(|_| async { unreachable!() }); let node_runtime = FakeNodeRuntime::new(); let this = cx.add_model(|_| Self { + server_id: LanguageServerId(0), http: http.clone(), node_runtime, server: CopilotServer::Running(RunningCopilotServer { @@ -355,6 +367,7 @@ impl Copilot { } fn start_language_server( + new_server_id: LanguageServerId, http: Arc, node_runtime: Arc, this: ModelHandle, @@ -369,13 +382,8 @@ impl Copilot { path: node_path, arguments, }; - let server = LanguageServer::new( - LanguageServerId(0), - binary, - Path::new("/"), - None, - cx.clone(), - )?; + let server = + LanguageServer::new(new_server_id, binary, Path::new("/"), None, cx.clone())?; server .on_notification::(|params, _cx| { @@ -547,9 +555,10 @@ impl Copilot { .spawn({ let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); + let server_id = self.server_id; move |this, cx| async move { clear_copilot_dir().await; - Self::start_language_server(http, node_runtime, this, cx).await + Self::start_language_server(server_id, http, node_runtime, this, cx).await } }) .shared(); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 07bea434e0..7d113a88af 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1018,6 +1018,10 @@ impl LanguageRegistry { .log_err(); }) } + + pub fn next_language_server_id(&self) -> LanguageServerId { + self.state.write().next_language_server_id() + } } impl LanguageRegistryState { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index c800e4e110..d22e26c1f5 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -129,6 +129,7 @@ fn main() { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); + let copilot_language_server_id = languages.next_language_server_id(); languages.set_executor(cx.background().clone()); languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); @@ -159,7 +160,7 @@ fn main() { semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx); vim::init(cx); terminal_view::init(cx); - copilot::init(http.clone(), node_runtime, cx); + copilot::init(copilot_language_server_id, http.clone(), node_runtime, cx); ai::init(cx); component_test::init(cx); From 9eadfc80bae7755e1e8648f5f3059935d6e80450 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 19 Sep 2023 16:25:07 +0300 Subject: [PATCH 3/8] Add Copilot server to LSP logs panel --- crates/copilot/src/copilot.rs | 8 ++++++++ crates/project/src/project.rs | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 4551fe0358..0361c9b46c 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -572,6 +572,14 @@ impl Copilot { cx.foreground().spawn(start_task) } + pub fn language_server(&self) -> Option<&Arc> { + if let CopilotServer::Running(server) = &self.server { + Some(&server.lsp) + } else { + None + } + } + pub fn register_buffer(&mut self, buffer: &ModelHandle, cx: &mut ModelContext) { let weak_buffer = buffer.downgrade(); self.buffers.insert(weak_buffer.clone()); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 27f3e45a05..2aa73fa52c 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -108,6 +108,7 @@ pub struct Project { active_entry: Option, buffer_ordered_messages_tx: mpsc::UnboundedSender, languages: Arc, + supplementary_language_servers: HashMap>, language_servers: HashMap, language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>, language_server_statuses: BTreeMap, @@ -647,6 +648,7 @@ impl Project { fs, next_entry_id: Default::default(), next_diagnostic_group_id: Default::default(), + supplementary_language_servers: HashMap::default(), language_servers: Default::default(), language_server_ids: Default::default(), language_server_statuses: Default::default(), @@ -723,6 +725,7 @@ impl Project { remote_id, replica_id, }), + supplementary_language_servers: HashMap::default(), language_servers: Default::default(), language_server_ids: Default::default(), language_server_statuses: response @@ -1915,6 +1918,7 @@ impl Project { self.detect_language_for_buffer(buffer, cx); self.register_buffer_with_language_servers(buffer, cx); self.register_buffer_with_copilot(buffer, cx); + self.register_copilot_language_server(cx); cx.observe_release(buffer, |this, buffer, cx| { if let Some(file) = File::from_dyn(buffer.file()) { if file.is_local() { @@ -2067,6 +2071,20 @@ impl Project { } } + fn register_copilot_language_server(&mut self, cx: &mut ModelContext) { + if let Some(copilot_language_server) = + Copilot::global(cx).and_then(|copilot| copilot.read(cx).language_server()) + { + let new_server_id = copilot_language_server.server_id(); + if let hash_map::Entry::Vacant(v) = + self.supplementary_language_servers.entry(new_server_id) + { + v.insert(Arc::clone(copilot_language_server)); + cx.emit(Event::LanguageServerAdded(new_server_id)) + } + } + } + async fn send_buffer_ordered_messages( this: WeakModelHandle, rx: UnboundedReceiver, @@ -7945,6 +7963,8 @@ impl Project { pub fn language_server_for_id(&self, id: LanguageServerId) -> Option> { if let LanguageServerState::Running { server, .. } = self.language_servers.get(&id)? { Some(server.clone()) + } else if let Some(server) = self.supplementary_language_servers.get(&id) { + Some(Arc::clone(server)) } else { None } From 556f398780060c4a828b759977347ce3aca7c647 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 19 Sep 2023 21:53:31 +0300 Subject: [PATCH 4/8] Send and receive Copilot events --- crates/copilot/src/copilot.rs | 7 ++++- crates/project/src/project.rs | 59 +++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 0361c9b46c..18ffb08867 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -276,8 +276,12 @@ pub struct Copilot { server_id: LanguageServerId, } +pub enum Event { + CopilotReady, +} + impl Entity for Copilot { - type Event = (); + type Event = Event; fn app_will_quit( &mut self, @@ -881,6 +885,7 @@ impl Copilot { self.register_buffer(&buffer, cx); } } + cx.emit(Event::CopilotReady); } request::SignInStatus::NotAuthorized { .. } => { server.sign_in_status = SignInStatus::Unauthorized; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 2aa73fa52c..c1c5e41dba 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -148,7 +148,7 @@ pub struct Project { _maintain_buffer_languages: Task<()>, _maintain_workspace_config: Task<()>, terminals: Terminals, - copilot_enabled: bool, + copilot_lsp_subscription: Option, current_lsp_settings: HashMap, LspSettings>, } @@ -619,6 +619,8 @@ impl Project { let (tx, rx) = mpsc::unbounded(); cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx)) .detach(); + let copilot_lsp_subscription = + Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx)); Self { worktrees: Default::default(), buffer_ordered_messages_tx: tx, @@ -660,7 +662,7 @@ impl Project { terminals: Terminals { local_handles: Vec::new(), }, - copilot_enabled: Copilot::global(cx).is_some(), + copilot_lsp_subscription, current_lsp_settings: settings::get::(cx).lsp.clone(), } }) @@ -696,6 +698,8 @@ impl Project { let (tx, rx) = mpsc::unbounded(); cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx)) .detach(); + let copilot_lsp_subscription = + Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx)); let mut this = Self { worktrees: Vec::new(), buffer_ordered_messages_tx: tx, @@ -754,7 +758,7 @@ impl Project { terminals: Terminals { local_handles: Vec::new(), }, - copilot_enabled: Copilot::global(cx).is_some(), + copilot_lsp_subscription, current_lsp_settings: settings::get::(cx).lsp.clone(), }; for worktree in worktrees { @@ -885,12 +889,14 @@ impl Project { self.restart_language_servers(worktree, language, cx); } - if !self.copilot_enabled && Copilot::global(cx).is_some() { - self.copilot_enabled = true; - for buffer in self.opened_buffers.values() { - if let Some(buffer) = buffer.upgrade(cx) { - self.register_buffer_with_copilot(&buffer, cx); + if self.copilot_lsp_subscription.is_none() { + if let Some(copilot) = Copilot::global(cx) { + for buffer in self.opened_buffers.values() { + if let Some(buffer) = buffer.upgrade(cx) { + self.register_buffer_with_copilot(&buffer, cx); + } } + self.copilot_lsp_subscription = Some(subscribe_for_copilot_events(&copilot, cx)); } } @@ -1918,7 +1924,6 @@ impl Project { self.detect_language_for_buffer(buffer, cx); self.register_buffer_with_language_servers(buffer, cx); self.register_buffer_with_copilot(buffer, cx); - self.register_copilot_language_server(cx); cx.observe_release(buffer, |this, buffer, cx| { if let Some(file) = File::from_dyn(buffer.file()) { if file.is_local() { @@ -2071,20 +2076,6 @@ impl Project { } } - fn register_copilot_language_server(&mut self, cx: &mut ModelContext) { - if let Some(copilot_language_server) = - Copilot::global(cx).and_then(|copilot| copilot.read(cx).language_server()) - { - let new_server_id = copilot_language_server.server_id(); - if let hash_map::Entry::Vacant(v) = - self.supplementary_language_servers.entry(new_server_id) - { - v.insert(Arc::clone(copilot_language_server)); - cx.emit(Event::LanguageServerAdded(new_server_id)) - } - } - } - async fn send_buffer_ordered_messages( this: WeakModelHandle, rx: UnboundedReceiver, @@ -8024,6 +8015,28 @@ impl Project { } } +fn subscribe_for_copilot_events( + copilot: &ModelHandle, + cx: &mut ModelContext<'_, Project>, +) -> gpui::Subscription { + cx.subscribe( + copilot, + |project, copilot, copilot_event, cx| match copilot_event { + copilot::Event::CopilotReady => { + if let Some(copilot_server) = copilot.read(cx).language_server() { + let new_server_id = copilot_server.server_id(); + if let hash_map::Entry::Vacant(v) = + project.supplementary_language_servers.entry(new_server_id) + { + v.insert(Arc::clone(copilot_server)); + cx.emit(Event::LanguageServerAdded(new_server_id)) + } + } + } + }, + ) +} + fn glob_literal_prefix<'a>(glob: &'a str) -> &'a str { let mut literal_end = 0; for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() { From 5e1b28484610704256bdfe9163cea5a0df7c5679 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 19 Sep 2023 22:29:27 +0300 Subject: [PATCH 5/8] Show supplementary language servers in the logs panel --- crates/copilot/src/copilot.rs | 11 ++++--- crates/language_tools/src/lsp_log.rs | 36 ++++++++++++++++------ crates/language_tools/src/lsp_log_tests.rs | 9 +++++- crates/project/src/project.rs | 23 +++++++++++--- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 18ffb08867..d36d5456be 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -11,8 +11,8 @@ use gpui::{ }; use language::{ language_settings::{all_language_settings, language_settings}, - point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16, - ToPointUtf16, + point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, + LanguageServerName, PointUtf16, ToPointUtf16, }; use log::{debug, error}; use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId}; @@ -130,6 +130,7 @@ impl CopilotServer { } struct RunningCopilotServer { + name: LanguageServerName, lsp: Arc, sign_in_status: SignInStatus, registered_buffers: HashMap, @@ -361,6 +362,7 @@ impl Copilot { http: http.clone(), node_runtime, server: CopilotServer::Running(RunningCopilotServer { + name: LanguageServerName(Arc::from("copilot")), lsp: Arc::new(server), sign_in_status: SignInStatus::Authorized, registered_buffers: Default::default(), @@ -439,6 +441,7 @@ impl Copilot { match server { Ok((server, status)) => { this.server = CopilotServer::Running(RunningCopilotServer { + name: LanguageServerName(Arc::from("copilot")), lsp: server, sign_in_status: SignInStatus::SignedOut, registered_buffers: Default::default(), @@ -576,9 +579,9 @@ impl Copilot { cx.foreground().spawn(start_task) } - pub fn language_server(&self) -> Option<&Arc> { + pub fn language_server(&self) -> Option<(&LanguageServerName, &Arc)> { if let CopilotServer::Running(server) = &self.server { - Some(&server.lsp) + Some((&server.name, &server.lsp)) } else { None } diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index 95b1bbd55f..d0a76610e4 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -13,7 +13,7 @@ use gpui::{ }; use language::{Buffer, LanguageServerId, LanguageServerName}; use lsp::IoKind; -use project::{search::SearchQuery, Project, Worktree}; +use project::{search::SearchQuery, Project}; use std::{borrow::Cow, sync::Arc}; use theme::{ui, Theme}; use workspace::{ @@ -70,7 +70,7 @@ enum MessageKind { pub(crate) struct LogMenuItem { pub server_id: LanguageServerId, pub server_name: LanguageServerName, - pub worktree: ModelHandle, + pub worktree_root_name: String, pub rpc_trace_enabled: bool, pub rpc_trace_selected: bool, pub logs_selected: bool, @@ -203,7 +203,6 @@ impl LogStore { } }) }); - Some(server_state.log_buffer.clone()) } @@ -410,7 +409,7 @@ impl LspLogView { Some(LogMenuItem { server_id, server_name: language_server_name, - worktree, + worktree_root_name: worktree.read(cx).root_name().to_string(), rpc_trace_enabled: state.rpc_state.is_some(), rpc_trace_selected: self.is_showing_rpc_trace && self.current_server_id == Some(server_id), @@ -418,6 +417,24 @@ impl LspLogView { && self.current_server_id == Some(server_id), }) }) + .chain( + self.project + .read(cx) + .supplementary_language_servers() + .filter_map(|(&server_id, (name, _))| { + let state = state.servers.get(&server_id)?; + Some(LogMenuItem { + server_id, + server_name: name.clone(), + worktree_root_name: "supplementary".to_string(), + 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::>(); rows.sort_by_key(|row| row.server_id); rows.dedup_by_key(|row| row.server_id); @@ -635,7 +652,7 @@ impl View for LspLogToolbarItemView { Self::render_language_server_menu_item( row.server_id, row.server_name, - row.worktree, + &row.worktree_root_name, row.rpc_trace_enabled, row.logs_selected, row.rpc_trace_selected, @@ -767,15 +784,14 @@ impl LspLogToolbarItemView { cx: &mut ViewContext, ) -> impl Element { enum ToggleMenu {} - MouseEventHandler::new::(0, cx, move |state, cx| { + MouseEventHandler::new::(0, cx, move |state, _| { let label: Cow = current_server .and_then(|row| { - let worktree = row.worktree.read(cx); Some( format!( "{} ({}) - {}", row.server_name.0, - worktree.root_name(), + row.worktree_root_name, if row.rpc_trace_selected { RPC_MESSAGES } else { @@ -800,7 +816,7 @@ impl LspLogToolbarItemView { fn render_language_server_menu_item( id: LanguageServerId, name: LanguageServerName, - worktree: ModelHandle, + worktree_root_name: &str, rpc_trace_enabled: bool, logs_selected: bool, rpc_trace_selected: bool, @@ -814,7 +830,7 @@ impl LspLogToolbarItemView { .with_child({ let style = &theme.toolbar_dropdown_menu.section_header; Label::new( - format!("{} ({})", name.0, worktree.read(cx).root_name()), + format!("{} ({})", name.0, worktree_root_name), style.text.clone(), ) .contained() diff --git a/crates/language_tools/src/lsp_log_tests.rs b/crates/language_tools/src/lsp_log_tests.rs index d26000ebc7..0830c3dac9 100644 --- a/crates/language_tools/src/lsp_log_tests.rs +++ b/crates/language_tools/src/lsp_log_tests.rs @@ -77,7 +77,14 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { &[LogMenuItem { server_id: language_server.server.server_id(), server_name: LanguageServerName("the-rust-language-server".into()), - worktree: project.read(cx).worktrees(cx).next().unwrap(), + worktree_root_name: project + .read(cx) + .worktrees(cx) + .next() + .unwrap() + .read(cx) + .root_name() + .to_string(), rpc_trace_enabled: false, rpc_trace_selected: false, logs_selected: true, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c1c5e41dba..b54296ee2a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -108,7 +108,8 @@ pub struct Project { active_entry: Option, buffer_ordered_messages_tx: mpsc::UnboundedSender, languages: Arc, - supplementary_language_servers: HashMap>, + supplementary_language_servers: + HashMap)>, language_servers: HashMap, language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>, language_server_statuses: BTreeMap, @@ -7951,10 +7952,22 @@ impl Project { }) } + pub fn supplementary_language_servers( + &self, + ) -> impl '_ + + Iterator< + Item = ( + &LanguageServerId, + &(LanguageServerName, Arc), + ), + > { + self.supplementary_language_servers.iter() + } + pub fn language_server_for_id(&self, id: LanguageServerId) -> Option> { if let LanguageServerState::Running { server, .. } = self.language_servers.get(&id)? { Some(server.clone()) - } else if let Some(server) = self.supplementary_language_servers.get(&id) { + } else if let Some((_, server)) = self.supplementary_language_servers.get(&id) { Some(Arc::clone(server)) } else { None @@ -8023,13 +8036,13 @@ fn subscribe_for_copilot_events( copilot, |project, copilot, copilot_event, cx| match copilot_event { copilot::Event::CopilotReady => { - if let Some(copilot_server) = copilot.read(cx).language_server() { + if let Some((name, copilot_server)) = copilot.read(cx).language_server() { let new_server_id = copilot_server.server_id(); if let hash_map::Entry::Vacant(v) = project.supplementary_language_servers.entry(new_server_id) { - v.insert(Arc::clone(copilot_server)); - cx.emit(Event::LanguageServerAdded(new_server_id)) + v.insert((name.clone(), Arc::clone(copilot_server))); + cx.emit(Event::LanguageServerAdded(new_server_id)); } } } From 7bc4f0bc11e2e25b2553b9947ce930c5861eae86 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 19 Sep 2023 23:41:55 +0300 Subject: [PATCH 6/8] Send copilot log messages into the log panel --- crates/copilot/src/copilot.rs | 17 +---------------- crates/project/src/project.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index d36d5456be..29b92ec361 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -14,10 +14,9 @@ use language::{ point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, LanguageServerName, PointUtf16, ToPointUtf16, }; -use log::{debug, error}; use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId}; use node_runtime::NodeRuntime; -use request::{LogMessage, StatusNotification}; +use request::StatusNotification; use settings::SettingsStore; use smol::{fs, io::BufReader, stream::StreamExt}; use std::{ @@ -391,20 +390,6 @@ impl Copilot { let server = LanguageServer::new(new_server_id, binary, Path::new("/"), None, cx.clone())?; - server - .on_notification::(|params, _cx| { - match params.level { - // Copilot is pretty aggressive about logging - 0 => debug!("copilot: {}", params.message), - 1 => debug!("copilot: {}", params.message), - _ => error!("copilot: {}", params.message), - } - - debug!("copilot metadata: {}", params.metadata_str); - debug!("copilot extra: {:?}", params.extra); - }) - .detach(); - server .on_notification::( |_, _| { /* Silence the notification */ }, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b54296ee2a..42f26a6541 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -150,6 +150,7 @@ pub struct Project { _maintain_workspace_config: Task<()>, terminals: Terminals, copilot_lsp_subscription: Option, + copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, } @@ -664,6 +665,7 @@ impl Project { local_handles: Vec::new(), }, copilot_lsp_subscription, + copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), } }) @@ -760,6 +762,7 @@ impl Project { local_handles: Vec::new(), }, copilot_lsp_subscription, + copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), }; for worktree in worktrees { @@ -8041,6 +8044,21 @@ fn subscribe_for_copilot_events( if let hash_map::Entry::Vacant(v) = project.supplementary_language_servers.entry(new_server_id) { + let weak_project = cx.weak_handle(); + let copilot_log_subscription = copilot_server + .on_notification::( + move |params, mut cx| { + if let Some(project) = weak_project.upgrade(&mut cx) { + project.update(&mut cx, |_, cx| { + cx.emit(Event::LanguageServerLog( + new_server_id, + params.message, + )); + }) + } + }, + ); + project.copilot_log_subscription = Some(copilot_log_subscription); v.insert((name.clone(), Arc::clone(copilot_server))); cx.emit(Event::LanguageServerAdded(new_server_id)); } From 82010b68c1088466f83a7d567e899764c7fd4861 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 20 Sep 2023 11:27:52 +0300 Subject: [PATCH 7/8] Avoid extra LSP log events --- crates/language_tools/src/lsp_log.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index d0a76610e4..48be24e22f 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -190,14 +190,17 @@ impl LogStore { .ok(); }) }); + let this = cx.weak_handle(); let weak_project = project.downgrade(); server_state._lsp_logs_subscription = server.map(|server| { let server_id = server.server_id(); server.on_notification::({ move |params, mut cx| { - if let Some(project) = weak_project.upgrade(&cx) { - project.update(&mut cx, |_, cx| { - cx.emit(project::Event::LanguageServerLog(server_id, params.message)) + if let Some((project, this)) = + weak_project.upgrade(&mut cx).zip(this.upgrade(&mut cx)) + { + this.update(&mut cx, |this, cx| { + this.add_language_server_log(&project, server_id, ¶ms.message, cx); }); } } @@ -309,19 +312,15 @@ impl LogStore { language_server_id: LanguageServerId, io_kind: IoKind, message: &str, - cx: &mut AppContext, + cx: &mut ModelContext, ) -> Option<()> { let is_received = match io_kind { IoKind::StdOut => true, IoKind::StdIn => false, IoKind::StdErr => { let project = project.upgrade(cx)?; - project.update(cx, |_, cx| { - cx.emit(project::Event::LanguageServerLog( - language_server_id, - format!("stderr: {}\n", message.trim()), - )) - }); + let message = format!("stderr: {}\n", message.trim()); + self.add_language_server_log(&project, language_server_id, &message, cx); return Some(()); } }; From 6ebe49ec591cc937f9346ddd0cae724588e80e88 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 20 Sep 2023 12:08:32 +0300 Subject: [PATCH 8/8] Show Copilot logs right after its LSP server start --- crates/copilot/src/copilot.rs | 4 ++-- crates/project/src/project.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 29b92ec361..4b81bebc02 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -277,7 +277,7 @@ pub struct Copilot { } pub enum Event { - CopilotReady, + CopilotLanguageServerStarted, } impl Entity for Copilot { @@ -431,6 +431,7 @@ impl Copilot { sign_in_status: SignInStatus::SignedOut, registered_buffers: Default::default(), }); + cx.emit(Event::CopilotLanguageServerStarted); this.update_sign_in_status(status, cx); } Err(error) => { @@ -873,7 +874,6 @@ impl Copilot { self.register_buffer(&buffer, cx); } } - cx.emit(Event::CopilotReady); } request::SignInStatus::NotAuthorized { .. } => { server.sign_in_status = SignInStatus::Unauthorized; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 42f26a6541..7b6aba72c4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -8038,7 +8038,7 @@ fn subscribe_for_copilot_events( cx.subscribe( copilot, |project, copilot, copilot_event, cx| match copilot_event { - copilot::Event::CopilotReady => { + copilot::Event::CopilotLanguageServerStarted => { if let Some((name, copilot_server)) = copilot.read(cx).language_server() { let new_server_id = copilot_server.server_id(); if let hash_map::Entry::Vacant(v) =