diff --git a/Cargo.lock b/Cargo.lock index d0af85057d..0ce1c9bd97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9058,6 +9058,7 @@ dependencies = [ "http_client", "language", "log", + "lsp", "node_runtime", "project", "remote", diff --git a/crates/assistant/src/context.rs b/crates/assistant/src/context.rs index 7702207d8d..38ccddb962 100644 --- a/crates/assistant/src/context.rs +++ b/crates/assistant/src/context.rs @@ -997,14 +997,14 @@ impl Context { fn handle_buffer_event( &mut self, _: Model, - event: &language::Event, + event: &language::BufferEvent, cx: &mut ModelContext, ) { match event { - language::Event::Operation(operation) => cx.emit(ContextEvent::Operation( + language::BufferEvent::Operation(operation) => cx.emit(ContextEvent::Operation( ContextOperation::BufferOperation(operation.clone()), )), - language::Event::Edited => { + language::BufferEvent::Edited => { self.count_remaining_tokens(cx); self.reparse(cx); // Use `inclusive = true` to invalidate a step when an edit occurs diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 1251668529..3c36c5f877 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1200,6 +1200,7 @@ impl Room { room_id: self.id(), worktrees: vec![], dev_server_project_id: Some(dev_server_project_id.0), + is_ssh_project: false, }) } else { if let Some(project_id) = project.read(cx).remote_id() { @@ -1210,6 +1211,7 @@ impl Room { room_id: self.id(), worktrees: project.read(cx).worktree_metadata_protos(cx), dev_server_project_id: None, + is_ssh_project: project.read(cx).is_via_ssh(), }) }; diff --git a/crates/channel/src/channel_buffer.rs b/crates/channel/src/channel_buffer.rs index 2041b16a6d..df3e66483f 100644 --- a/crates/channel/src/channel_buffer.rs +++ b/crates/channel/src/channel_buffer.rs @@ -171,11 +171,11 @@ impl ChannelBuffer { fn on_buffer_update( &mut self, _: Model, - event: &language::Event, + event: &language::BufferEvent, cx: &mut ModelContext, ) { match event { - language::Event::Operation(operation) => { + language::BufferEvent::Operation(operation) => { if *ZED_ALWAYS_ACTIVE { if let language::Operation::UpdateSelections { selections, .. } = operation { if selections.is_empty() { @@ -191,7 +191,7 @@ impl ChannelBuffer { }) .log_err(); } - language::Event::Edited => { + language::BufferEvent::Edited => { cx.emit(ChannelBufferEvent::BufferEdited); } _ => {} diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index c6db54b572..b514d4bb03 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -30,6 +30,7 @@ impl Database { room_id: RoomId, connection: ConnectionId, worktrees: &[proto::WorktreeMetadata], + is_ssh_project: bool, dev_server_project_id: Option, ) -> Result> { self.room_transaction(room_id, |tx| async move { @@ -121,12 +122,14 @@ impl Database { .await?; } + let replica_id = if is_ssh_project { 1 } else { 0 }; + project_collaborator::ActiveModel { project_id: ActiveValue::set(project.id), connection_id: ActiveValue::set(connection.id as i32), connection_server_id: ActiveValue::set(ServerId(connection.owner_id as i32)), user_id: ActiveValue::set(participant.user_id), - replica_id: ActiveValue::set(ReplicaId(0)), + replica_id: ActiveValue::set(ReplicaId(replica_id)), is_host: ActiveValue::set(true), ..Default::default() } diff --git a/crates/collab/src/db/tests/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs index 9a4ca3c11a..6263350287 100644 --- a/crates/collab/src/db/tests/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -540,18 +540,18 @@ async fn test_project_count(db: &Arc) { .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0); - db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], None) + db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, None) .await .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1); - db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], None) + db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, None) .await .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2); // Projects shared by admins aren't counted. - db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], None) + db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false, None) .await .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 7db446b2b8..4146eafb87 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1996,6 +1996,7 @@ async fn share_project( RoomId::from_proto(request.room_id), session.connection_id, &request.worktrees, + request.is_ssh_project, request .dev_server_project_id .map(DevServerProjectId::from_proto), diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs index 3f205b7f93..de03144774 100644 --- a/crates/collab/src/tests/editor_tests.rs +++ b/crates/collab/src/tests/editor_tests.rs @@ -284,7 +284,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu let active_call_a = cx_a.read(ActiveCall::global); client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -552,7 +552,7 @@ async fn test_collaborating_with_code_actions( client_a.language_registry().add(rust_lang()); let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + .register_fake_lsp("Rust", FakeLspAdapter::default()); client_a .fs() @@ -757,7 +757,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T // Set up a fake language server. client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -982,7 +982,7 @@ async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut Tes cx_b.update(editor::init); client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { name: "the-language-server", @@ -1268,7 +1268,7 @@ async fn test_on_input_format_from_host_to_guest( let active_call_a = cx_a.read(ActiveCall::global); client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -1388,7 +1388,7 @@ async fn test_on_input_format_from_guest_to_host( let active_call_a = cx_a.read(ActiveCall::global); client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -1545,7 +1545,7 @@ async fn test_mutual_editor_inlay_hint_cache_update( client_a.language_registry().add(rust_lang()); client_b.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -1807,7 +1807,7 @@ async fn test_inlay_hint_refresh_is_forwarded( client_a.language_registry().add(rust_lang()); client_b.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index b6d7aca2e0..245d0eda1b 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -3859,7 +3859,7 @@ async fn test_collaborating_with_diagnostics( ))); let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", Default::default()); + .register_fake_lsp("Rust", Default::default()); // Share a project as client A client_a @@ -4126,7 +4126,7 @@ async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering( .await; client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()), @@ -4349,7 +4349,7 @@ async fn test_formatting_buffer( client_a.language_registry().add(rust_lang()); let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + .register_fake_lsp("Rust", FakeLspAdapter::default()); // Here we insert a fake tree with a directory that exists on disk. This is needed // because later we'll invoke a command, which requires passing a working directory @@ -4460,7 +4460,7 @@ async fn test_prettier_formatting_buffer( }, Some(tree_sitter_rust::language()), ))); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "TypeScript", FakeLspAdapter { prettier_plugins: vec![test_plugin], @@ -4576,7 +4576,7 @@ async fn test_definition( let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", Default::default()); + .register_fake_lsp("Rust", Default::default()); client_a.language_registry().add(rust_lang()); client_a @@ -4712,7 +4712,7 @@ async fn test_references( let active_call_a = cx_a.read(ActiveCall::global); client_a.language_registry().add(rust_lang()); - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { name: "my-fake-lsp-adapter", @@ -4983,7 +4983,7 @@ async fn test_document_highlights( let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", Default::default()); + .register_fake_lsp("Rust", Default::default()); client_a.language_registry().add(rust_lang()); let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; @@ -5079,28 +5079,30 @@ async fn test_lsp_hover( client_a.language_registry().add(rust_lang()); let language_server_names = ["rust-analyzer", "CrabLang-ls"]; - let mut fake_language_servers = client_a.language_registry().register_fake_lsp_adapter( - "Rust", - FakeLspAdapter { - name: "rust-analyzer", - capabilities: lsp::ServerCapabilities { - hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + let mut language_servers = [ + client_a.language_registry().register_fake_lsp( + "Rust", + FakeLspAdapter { + name: "rust-analyzer", + capabilities: lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _other_server = client_a.language_registry().register_fake_lsp_adapter( - "Rust", - FakeLspAdapter { - name: "CrabLang-ls", - capabilities: lsp::ServerCapabilities { - hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + ), + client_a.language_registry().register_fake_lsp( + "Rust", + FakeLspAdapter { + name: "CrabLang-ls", + capabilities: lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); + ), + ]; let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; let project_id = active_call_a @@ -5115,7 +5117,7 @@ async fn test_lsp_hover( let mut servers_with_hover_requests = HashMap::default(); for i in 0..language_server_names.len() { - let new_server = fake_language_servers.next().await.unwrap_or_else(|| { + let new_server = language_servers[i].next().await.unwrap_or_else(|| { panic!( "Failed to get language server #{i} with name {}", &language_server_names[i] @@ -5260,7 +5262,7 @@ async fn test_project_symbols( client_a.language_registry().add(rust_lang()); let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", Default::default()); + .register_fake_lsp("Rust", Default::default()); client_a .fs() @@ -5362,7 +5364,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( client_a.language_registry().add(rust_lang()); let mut fake_language_servers = client_a .language_registry() - .register_fake_lsp_adapter("Rust", Default::default()); + .register_fake_lsp("Rust", Default::default()); client_a .fs() diff --git a/crates/collab/src/tests/random_project_collaboration_tests.rs b/crates/collab/src/tests/random_project_collaboration_tests.rs index 2324f03cd4..831114ba1a 100644 --- a/crates/collab/src/tests/random_project_collaboration_tests.rs +++ b/crates/collab/src/tests/random_project_collaboration_tests.rs @@ -1047,7 +1047,7 @@ impl RandomizedTest for ProjectCollaborationTest { }, None, ))); - client.language_registry().register_fake_lsp_adapter( + client.language_registry().register_fake_lsp( "Rust", FakeLspAdapter { name: "the-fake-language-server", diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 028e148cba..38aaf2faa3 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -228,10 +228,10 @@ impl MessageEditor { fn on_buffer_event( &mut self, buffer: Model, - event: &language::Event, + event: &language::BufferEvent, cx: &mut ViewContext, ) { - if let language::Event::Reparsed | language::Event::Edited = event { + if let language::BufferEvent::Reparsed | language::BufferEvent::Edited = event { let buffer = buffer.read(cx).snapshot(); self.mentions_task = Some(cx.spawn(|this, cx| async move { cx.background_executor() diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 5fc86480e6..cdbe65ba1d 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -691,17 +691,17 @@ impl Copilot { fn handle_buffer_event( &mut self, buffer: Model, - event: &language::Event, + event: &language::BufferEvent, cx: &mut ModelContext, ) -> Result<()> { if let Ok(server) = self.server.as_running() { if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.entity_id()) { match event { - language::Event::Edited => { + language::BufferEvent::Edited => { drop(registered_buffer.report_changes(&buffer, cx)); } - language::Event::Saved => { + language::BufferEvent::Saved => { server .lsp .notify::( @@ -713,7 +713,8 @@ impl Copilot { }, )?; } - language::Event::FileHandleChanged | language::Event::LanguageChanged => { + language::BufferEvent::FileHandleChanged + | language::BufferEvent::LanguageChanged => { let new_language_id = id_for_language(buffer.read(cx).language()); let new_uri = uri_for_buffer(&buffer, cx); if new_uri != registered_buffer.uri diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 0b1e0385de..b1ae40e651 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6205,7 +6205,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -6361,7 +6361,7 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -6557,7 +6557,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -6708,7 +6708,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { ..PrettierSettings::default() }); }); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -9488,7 +9488,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) { }, Some(tree_sitter_rust::language()), ))); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -9601,7 +9601,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test }, Some(tree_sitter_rust::language()), ))); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: language_server_name, @@ -9945,7 +9945,7 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { }); let test_plugin = "test_plugin"; - let _ = language_registry.register_fake_lsp_adapter( + let _ = language_registry.register_fake_lsp( "TypeScript", FakeLspAdapter { prettier_plugins: vec![test_plugin], diff --git a/crates/editor/src/git/blame.rs b/crates/editor/src/git/blame.rs index 0cb866fb24..00531ee886 100644 --- a/crates/editor/src/git/blame.rs +++ b/crates/editor/src/git/blame.rs @@ -121,12 +121,12 @@ impl GitBlame { ); let buffer_subscriptions = cx.subscribe(&buffer, |this, buffer, event, cx| match event { - language::Event::DirtyChanged => { + language::BufferEvent::DirtyChanged => { if !buffer.read(cx).is_dirty() { this.generate(cx); } } - language::Event::Edited => { + language::BufferEvent::Edited => { this.regenerate_on_edit(cx); } _ => {} diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 01b681dd45..b03674dc48 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -1577,7 +1577,7 @@ pub mod tests { }, Some(tree_sitter_rust::language()), ))); - let fake_servers = language_registry.register_fake_lsp_adapter( + let fake_servers = language_registry.register_fake_lsp( name, FakeLspAdapter { name, @@ -2273,7 +2273,7 @@ pub mod tests { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(crate::editor_tests::rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -2569,7 +2569,7 @@ pub mod tests { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language = crate::editor_tests::rust_lang(); language_registry.add(language); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -2919,7 +2919,7 @@ pub mod tests { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(crate::editor_tests::rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -3148,7 +3148,7 @@ pub mod tests { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(crate::editor_tests::rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -3389,7 +3389,7 @@ pub mod tests { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(crate::editor_tests::rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index 16735760bf..17959ab50b 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -57,7 +57,7 @@ impl EditorLspTestContext { let project = Project::test(app_state.fs.clone(), [], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( language.name(), FakeLspAdapter { capabilities, diff --git a/crates/extension/src/extension_store_test.rs b/crates/extension/src/extension_store_test.rs index da530306d1..326c713bd5 100644 --- a/crates/extension/src/extension_store_test.rs +++ b/crates/extension/src/extension_store_test.rs @@ -609,7 +609,14 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { .await .unwrap(); - let mut fake_servers = language_registry.fake_language_servers("Gleam".into()); + let mut fake_servers = language_registry.register_fake_language_server( + LanguageServerName("gleam".into()), + lsp::ServerCapabilities { + completion_provider: Some(Default::default()), + ..Default::default() + }, + None, + ); let buffer = project .update(cx, |project, cx| { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 4e8af1ea8c..74080d0162 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -302,7 +302,7 @@ pub enum Operation { /// An event that occurs in a buffer. #[derive(Clone, Debug, PartialEq)] -pub enum Event { +pub enum BufferEvent { /// The buffer was changed in a way that must be /// propagated to its other replicas. Operation(Operation), @@ -809,7 +809,7 @@ impl Buffer { self.syntax_map.lock().clear(); self.language = language; self.reparse(cx); - cx.emit(Event::LanguageChanged); + cx.emit(BufferEvent::LanguageChanged); } /// Assign a language registry to the buffer. This allows the buffer to retrieve @@ -827,7 +827,7 @@ impl Buffer { /// Assign the buffer a new [Capability]. pub fn set_capability(&mut self, capability: Capability, cx: &mut ModelContext) { self.capability = capability; - cx.emit(Event::CapabilityChanged) + cx.emit(BufferEvent::CapabilityChanged) } /// This method is called to signal that the buffer has been saved. @@ -842,13 +842,13 @@ impl Buffer { .set((self.saved_version().clone(), false)); self.has_conflict = false; self.saved_mtime = mtime; - cx.emit(Event::Saved); + cx.emit(BufferEvent::Saved); cx.notify(); } /// This method is called to signal that the buffer has been discarded. pub fn discarded(&mut self, cx: &mut ModelContext) { - cx.emit(Event::Discarded); + cx.emit(BufferEvent::Discarded); cx.notify(); } @@ -911,7 +911,7 @@ impl Buffer { .set((self.saved_version.clone(), false)); self.text.set_line_ending(line_ending); self.saved_mtime = mtime; - cx.emit(Event::Reloaded); + cx.emit(BufferEvent::Reloaded); cx.notify(); } @@ -929,7 +929,7 @@ impl Buffer { if !old_file.is_deleted() { file_changed = true; if !self.is_dirty() { - cx.emit(Event::DirtyChanged); + cx.emit(BufferEvent::DirtyChanged); } } } else { @@ -949,7 +949,7 @@ impl Buffer { self.file = Some(new_file); if file_changed { self.non_text_state_update_count += 1; - cx.emit(Event::FileHandleChanged); + cx.emit(BufferEvent::FileHandleChanged); cx.notify(); } } @@ -974,7 +974,7 @@ impl Buffer { recalc_task.await; buffer .update(&mut cx, |_, cx| { - cx.emit(Event::DiffBaseChanged); + cx.emit(BufferEvent::DiffBaseChanged); }) .ok(); }) @@ -1003,7 +1003,7 @@ impl Buffer { this.update(&mut cx, |this, cx| { this.git_diff = buffer_diff; this.non_text_state_update_count += 1; - cx.emit(Event::DiffUpdated); + cx.emit(BufferEvent::DiffUpdated); }) .ok(); })) @@ -1142,7 +1142,7 @@ impl Buffer { self.syntax_map.lock().did_parse(syntax_snapshot); self.request_autoindent(cx); self.parse_status.0.send(ParseStatus::Idle).unwrap(); - cx.emit(Event::Reparsed); + cx.emit(BufferEvent::Reparsed); cx.notify(); } @@ -1900,9 +1900,9 @@ impl Buffer { self.reparse(cx); - cx.emit(Event::Edited); + cx.emit(BufferEvent::Edited); if was_dirty != self.is_dirty() { - cx.emit(Event::DirtyChanged); + cx.emit(BufferEvent::DirtyChanged); } cx.notify(); } @@ -2106,12 +2106,12 @@ impl Buffer { self.non_text_state_update_count += 1; self.text.lamport_clock.observe(lamport_timestamp); cx.notify(); - cx.emit(Event::DiagnosticsUpdated); + cx.emit(BufferEvent::DiagnosticsUpdated); } } fn send_operation(&mut self, operation: Operation, cx: &mut ModelContext) { - cx.emit(Event::Operation(operation)); + cx.emit(BufferEvent::Operation(operation)); } /// Removes the selections for a given peer. @@ -2300,7 +2300,7 @@ impl Buffer { } } -impl EventEmitter for Buffer {} +impl EventEmitter for Buffer {} impl Deref for Buffer { type Target = TextBuffer; diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 77a1079d3a..7e6f6ef368 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -275,7 +275,7 @@ fn test_edit_events(cx: &mut gpui::AppContext) { |buffer, cx| { let buffer_1_events = buffer_1_events.clone(); cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() { - Event::Operation(op) => buffer1_ops.lock().push(op), + BufferEvent::Operation(op) => buffer1_ops.lock().push(op), event => buffer_1_events.lock().push(event), }) .detach(); @@ -313,15 +313,15 @@ fn test_edit_events(cx: &mut gpui::AppContext) { assert_eq!( mem::take(&mut *buffer_1_events.lock()), vec![ - Event::Edited, - Event::DirtyChanged, - Event::Edited, - Event::Edited, + BufferEvent::Edited, + BufferEvent::DirtyChanged, + BufferEvent::Edited, + BufferEvent::Edited, ] ); assert_eq!( mem::take(&mut *buffer_2_events.lock()), - vec![Event::Edited, Event::DirtyChanged] + vec![BufferEvent::Edited, BufferEvent::DirtyChanged] ); buffer1.update(cx, |buffer, cx| { @@ -336,11 +336,11 @@ fn test_edit_events(cx: &mut gpui::AppContext) { }); assert_eq!( mem::take(&mut *buffer_1_events.lock()), - vec![Event::Edited, Event::DirtyChanged,] + vec![BufferEvent::Edited, BufferEvent::DirtyChanged,] ); assert_eq!( mem::take(&mut *buffer_2_events.lock()), - vec![Event::Edited, Event::DirtyChanged] + vec![BufferEvent::Edited, BufferEvent::DirtyChanged] ); } @@ -2411,7 +2411,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200))); let network = network.clone(); cx.subscribe(&cx.handle(), move |buffer, _, event, _| { - if let Event::Operation(op) = event { + if let BufferEvent::Operation(op) = event { network .lock() .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]); @@ -2539,7 +2539,7 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200))); let network = network.clone(); cx.subscribe(&cx.handle(), move |buffer, _, event, _| { - if let Event::Operation(op) = event { + if let BufferEvent::Operation(op) = event { network.lock().broadcast( buffer.replica_id(), vec![proto::serialize_operation(op)], diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 3112d88aa5..fa5d79b53f 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -270,11 +270,6 @@ impl CachedLspAdapter { .cloned() .unwrap_or_else(|| language_name.lsp_id()) } - - #[cfg(any(test, feature = "test-support"))] - fn as_fake(&self) -> Option<&FakeLspAdapter> { - self.adapter.as_fake() - } } /// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application @@ -508,11 +503,6 @@ pub trait LspAdapter: 'static + Send + Sync { fn language_ids(&self) -> HashMap { Default::default() } - - #[cfg(any(test, feature = "test-support"))] - fn as_fake(&self) -> Option<&FakeLspAdapter> { - None - } } async fn try_fetch_server_binary( @@ -751,12 +741,13 @@ where pub struct FakeLspAdapter { pub name: &'static str, pub initialization_options: Option, - pub capabilities: lsp::ServerCapabilities, - pub initializer: Option>, + pub prettier_plugins: Vec<&'static str>, pub disk_based_diagnostics_progress_token: Option, pub disk_based_diagnostics_sources: Vec, - pub prettier_plugins: Vec<&'static str>, pub language_server_binary: LanguageServerBinary, + + pub capabilities: lsp::ServerCapabilities, + pub initializer: Option>, } /// Configuration of handling bracket pairs for a given language. @@ -1717,10 +1708,6 @@ impl LspAdapter for FakeLspAdapter { ) -> Result> { Ok(self.initialization_options.clone()) } - - fn as_fake(&self) -> Option<&FakeLspAdapter> { - Some(self) - } } fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option)]) { diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 69e44ee4ac..e89b73f11b 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -99,10 +99,15 @@ struct LanguageRegistryState { reload_count: usize, #[cfg(any(test, feature = "test-support"))] - fake_server_txs: HashMap< - LanguageName, - Vec>, - >, + fake_server_entries: HashMap, +} + +#[cfg(any(test, feature = "test-support"))] +pub struct FakeLanguageServerEntry { + pub capabilities: lsp::ServerCapabilities, + pub initializer: Option>, + pub tx: futures::channel::mpsc::UnboundedSender, + pub _server: Option, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -220,7 +225,7 @@ impl LanguageRegistry { reload_count: 0, #[cfg(any(test, feature = "test-support"))] - fake_server_txs: Default::default(), + fake_server_entries: Default::default(), }), language_server_download_dir: None, lsp_binary_status_tx: Default::default(), @@ -330,12 +335,35 @@ impl LanguageRegistry { .push(CachedLspAdapter::new(adapter)); } + /// Register a fake language server and adapter + /// The returned channel receives a new instance of the language server every time it is started + #[cfg(any(feature = "test-support", test))] + pub fn register_fake_lsp( + &self, + language_name: impl Into, + mut adapter: crate::FakeLspAdapter, + ) -> futures::channel::mpsc::UnboundedReceiver { + let language_name = language_name.into(); + let adapter_name = LanguageServerName(adapter.name.into()); + let capabilities = adapter.capabilities.clone(); + let initializer = adapter.initializer.take(); + self.state + .write() + .lsp_adapters + .entry(language_name.clone()) + .or_default() + .push(CachedLspAdapter::new(Arc::new(adapter))); + self.register_fake_language_server(adapter_name, capabilities, initializer) + } + + /// Register a fake lsp adapter (without the language server) + /// The returned channel receives a new instance of the language server every time it is started #[cfg(any(feature = "test-support", test))] pub fn register_fake_lsp_adapter( &self, language_name: impl Into, adapter: crate::FakeLspAdapter, - ) -> futures::channel::mpsc::UnboundedReceiver { + ) { let language_name = language_name.into(); self.state .write() @@ -343,21 +371,27 @@ impl LanguageRegistry { .entry(language_name.clone()) .or_default() .push(CachedLspAdapter::new(Arc::new(adapter))); - self.fake_language_servers(language_name) } + /// Register a fake language server (without the adapter) + /// The returned channel receives a new instance of the language server every time it is started #[cfg(any(feature = "test-support", test))] - pub fn fake_language_servers( + pub fn register_fake_language_server( &self, - language_name: LanguageName, + lsp_name: LanguageServerName, + capabilities: lsp::ServerCapabilities, + initializer: Option>, ) -> futures::channel::mpsc::UnboundedReceiver { let (servers_tx, servers_rx) = futures::channel::mpsc::unbounded(); - self.state - .write() - .fake_server_txs - .entry(language_name) - .or_default() - .push(servers_tx); + self.state.write().fake_server_entries.insert( + lsp_name, + FakeLanguageServerEntry { + tx: servers_tx, + capabilities, + initializer, + _server: None, + }, + ); servers_rx } @@ -835,8 +869,8 @@ impl LanguageRegistry { adapter.name.0 ); - let download_dir = self - .language_server_download_dir + let download_dir = &self + .language_server_download_dir .clone() .ok_or_else(|| anyhow!("language server download directory has not been assigned before starting server")) .log_err()?; @@ -877,52 +911,43 @@ impl LanguageRegistry { #[cfg(any(test, feature = "test-support"))] if true { - let capabilities = adapter - .as_fake() - .map(|fake_adapter| fake_adapter.capabilities.clone()) - .unwrap_or_else(|| lsp::ServerCapabilities { - completion_provider: Some(Default::default()), - ..Default::default() - }); + if let Some(this) = this.upgrade() { + if let Some(fake_entry) = this + .state + .write() + .fake_server_entries + .get_mut(&adapter.name) + { + let (server, mut fake_server) = lsp::FakeLanguageServer::new( + server_id, + binary, + adapter.name.0.to_string(), + fake_entry.capabilities.clone(), + cx.clone(), + ); + fake_entry._server = Some(fake_server.clone()); - let (server, mut fake_server) = lsp::FakeLanguageServer::new( - server_id, - binary, - adapter.name.0.to_string(), - capabilities, - cx.clone(), - ); + if let Some(initializer) = &fake_entry.initializer { + initializer(&mut fake_server); + } - if let Some(fake_adapter) = adapter.as_fake() { - if let Some(initializer) = &fake_adapter.initializer { - initializer(&mut fake_server); + let tx = fake_entry.tx.clone(); + cx.background_executor() + .spawn(async move { + if fake_server + .try_receive_notification::( + ) + .await + .is_some() + { + tx.unbounded_send(fake_server.clone()).ok(); + } + }) + .detach(); + + return Ok((server, options)); } } - - cx.background_executor() - .spawn(async move { - if fake_server - .try_receive_notification::() - .await - .is_some() - { - if let Some(this) = this.upgrade() { - if let Some(txs) = this - .state - .write() - .fake_server_txs - .get_mut(&_language_name_for_tests) - { - for tx in txs { - tx.unbounded_send(fake_server.clone()).ok(); - } - } - } - } - }) - .detach(); - - return Ok((server, options)); } drop(this); diff --git a/crates/language_tools/src/lsp_log_tests.rs b/crates/language_tools/src/lsp_log_tests.rs index b465773691..524a8e9996 100644 --- a/crates/language_tools/src/lsp_log_tests.rs +++ b/crates/language_tools/src/lsp_log_tests.rs @@ -45,7 +45,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { }, Some(tree_sitter_rust::language()), ))); - let mut fake_rust_servers = language_registry.register_fake_lsp_adapter( + let mut fake_rust_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: "the-rust-language-server", diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 7910b77bd8..f7172ccf08 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -1673,31 +1673,33 @@ impl MultiBuffer { fn on_buffer_event( &mut self, buffer: Model, - event: &language::Event, + event: &language::BufferEvent, cx: &mut ModelContext, ) { cx.emit(match event { - language::Event::Edited => Event::Edited { + language::BufferEvent::Edited => Event::Edited { singleton_buffer_edited: true, }, - language::Event::DirtyChanged => Event::DirtyChanged, - language::Event::Saved => Event::Saved, - language::Event::FileHandleChanged => Event::FileHandleChanged, - language::Event::Reloaded => Event::Reloaded, - language::Event::DiffBaseChanged => Event::DiffBaseChanged, - language::Event::DiffUpdated => Event::DiffUpdated { buffer }, - language::Event::LanguageChanged => Event::LanguageChanged(buffer.read(cx).remote_id()), - language::Event::Reparsed => Event::Reparsed(buffer.read(cx).remote_id()), - language::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated, - language::Event::Closed => Event::Closed, - language::Event::Discarded => Event::Discarded, - language::Event::CapabilityChanged => { + language::BufferEvent::DirtyChanged => Event::DirtyChanged, + language::BufferEvent::Saved => Event::Saved, + language::BufferEvent::FileHandleChanged => Event::FileHandleChanged, + language::BufferEvent::Reloaded => Event::Reloaded, + language::BufferEvent::DiffBaseChanged => Event::DiffBaseChanged, + language::BufferEvent::DiffUpdated => Event::DiffUpdated { buffer }, + language::BufferEvent::LanguageChanged => { + Event::LanguageChanged(buffer.read(cx).remote_id()) + } + language::BufferEvent::Reparsed => Event::Reparsed(buffer.read(cx).remote_id()), + language::BufferEvent::DiagnosticsUpdated => Event::DiagnosticsUpdated, + language::BufferEvent::Closed => Event::Closed, + language::BufferEvent::Discarded => Event::Discarded, + language::BufferEvent::CapabilityChanged => { self.capability = buffer.read(cx).capability(); Event::CapabilityChanged } // - language::Event::Operation(_) => return, + language::BufferEvent::Operation(_) => return, }); } diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 403b53745a..ead3235997 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -15,7 +15,7 @@ use gpui::{ use http_client::Url; use language::{ proto::{deserialize_line_ending, deserialize_version, serialize_version, split_operations}, - Buffer, Capability, Event as BufferEvent, File as _, Language, Operation, + Buffer, BufferEvent, Capability, File as _, Language, Operation, }; use rpc::{proto, AnyProtoClient, ErrorExt as _, TypedEnvelope}; use smol::channel::Receiver; @@ -62,7 +62,7 @@ pub enum BufferStoreEvent { }, } -#[derive(Default)] +#[derive(Default, Debug)] pub struct ProjectTransaction(pub HashMap, language::Transaction>); impl EventEmitter for BufferStore {} @@ -1054,7 +1054,11 @@ impl BufferStore { buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?; } OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops), - OpenBuffer::Weak(_) => {} + OpenBuffer::Weak(buffer) => { + if let Some(buffer) = buffer.upgrade() { + buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?; + } + } }, hash_map::Entry::Vacant(e) => { e.insert(OpenBuffer::Operations(ops)); diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 748fa00aa1..2b7b10d9b3 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -46,8 +46,8 @@ pub fn lsp_formatting_options(settings: &LanguageSettings) -> lsp::FormattingOpt } #[async_trait(?Send)] -pub trait LspCommand: 'static + Sized + Send { - type Response: 'static + Default + Send; +pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug { + type Response: 'static + Default + Send + std::fmt::Debug; type LspRequest: 'static + Send + lsp::request::Request; type ProtoRequest: 'static + Send + proto::RequestMessage; @@ -104,72 +104,80 @@ pub trait LspCommand: 'static + Sized + Send { fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result; } +#[derive(Debug)] pub(crate) struct PrepareRename { pub position: PointUtf16, } +#[derive(Debug)] pub(crate) struct PerformRename { pub position: PointUtf16, pub new_name: String, pub push_to_history: bool, } +#[derive(Debug)] pub struct GetDefinition { pub position: PointUtf16, } +#[derive(Debug)] pub(crate) struct GetDeclaration { pub position: PointUtf16, } +#[derive(Debug)] pub(crate) struct GetTypeDefinition { pub position: PointUtf16, } +#[derive(Debug)] pub(crate) struct GetImplementation { pub position: PointUtf16, } - +#[derive(Debug)] pub(crate) struct GetReferences { pub position: PointUtf16, } +#[derive(Debug)] pub(crate) struct GetDocumentHighlights { pub position: PointUtf16, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct GetSignatureHelp { pub position: PointUtf16, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct GetHover { pub position: PointUtf16, } +#[derive(Debug)] pub(crate) struct GetCompletions { pub position: PointUtf16, pub context: CompletionContext, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct GetCodeActions { pub range: Range, pub kinds: Option>, } - +#[derive(Debug)] pub(crate) struct OnTypeFormatting { pub position: PointUtf16, pub trigger: String, pub options: lsp::FormattingOptions, pub push_to_history: bool, } - +#[derive(Debug)] pub(crate) struct InlayHints { pub range: Range, } - +#[derive(Debug)] pub(crate) struct LinkedEditingRange { pub position: Anchor, } diff --git a/crates/project/src/lsp_ext_command.rs b/crates/project/src/lsp_ext_command.rs index bf80917df9..9fa1dc5480 100644 --- a/crates/project/src/lsp_ext_command.rs +++ b/crates/project/src/lsp_ext_command.rs @@ -36,7 +36,7 @@ impl ExpandedMacro { self.name.is_empty() && self.expansion.is_empty() } } - +#[derive(Debug)] pub struct ExpandMacro { pub position: PointUtf16, } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 854915e82d..cea10094a9 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -501,15 +501,15 @@ impl LspStore { fn on_buffer_event( &mut self, buffer: Model, - event: &language::Event, + event: &language::BufferEvent, cx: &mut ModelContext, ) { match event { - language::Event::Edited { .. } => { + language::BufferEvent::Edited { .. } => { self.on_buffer_edited(buffer, cx); } - language::Event::Saved => { + language::BufferEvent::Saved => { self.on_buffer_saved(buffer, cx); } @@ -2930,6 +2930,7 @@ impl LspStore { }) .map(|(_, server)| server.server_id()) .collect::>(); + let mut response_results = server_ids .into_iter() .map(|server_id| { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 8fca2ea686..9185670b05 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -50,9 +50,9 @@ use language::{ deserialize_anchor, serialize_anchor, serialize_line_ending, serialize_version, split_operations, }, - Buffer, CachedLspAdapter, Capability, CodeLabel, ContextProvider, DiagnosticEntry, Diff, - Documentation, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, - PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped, + Buffer, BufferEvent, CachedLspAdapter, Capability, CodeLabel, ContextProvider, DiagnosticEntry, + Diff, Documentation, File as _, Language, LanguageRegistry, LanguageServerName, PointUtf16, + ToOffset, ToPointUtf16, Transaction, Unclipped, }; use lsp::{CompletionContext, DocumentHighlightKind, LanguageServer, LanguageServerId}; use lsp_command::*; @@ -799,6 +799,7 @@ impl Project { client.add_model_message_handler(Self::handle_create_buffer_for_peer); client.add_model_message_handler(BufferStore::handle_update_buffer_file); client.add_model_message_handler(BufferStore::handle_update_diff_base); + client.add_model_request_handler(BufferStore::handle_update_buffer); LspStore::init(&client); SettingsObserver::init(&client); @@ -1367,7 +1368,13 @@ impl Project { pub fn replica_id(&self) -> ReplicaId { match self.client_state { ProjectClientState::Remote { replica_id, .. } => replica_id, - _ => 0, + _ => { + if self.ssh_session.is_some() { + 1 + } else { + 0 + } + } } } @@ -1818,6 +1825,15 @@ impl Project { } } + pub fn is_via_ssh(&self) -> bool { + match &self.client_state { + ProjectClientState::Local | ProjectClientState::Shared { .. } => { + self.ssh_session.is_some() + } + ProjectClientState::Remote { .. } => false, + } + } + pub fn is_via_collab(&self) -> bool { match &self.client_state { ProjectClientState::Local | ProjectClientState::Shared { .. } => false, diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 4662c75477..83deb6fd91 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -315,7 +315,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); - let mut fake_rust_servers = language_registry.register_fake_lsp_adapter( + let mut fake_rust_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: "the-rust-language-server", @@ -335,7 +335,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { ..Default::default() }, ); - let mut fake_json_servers = language_registry.register_fake_lsp_adapter( + let mut fake_json_servers = language_registry.register_fake_lsp( "JSON", FakeLspAdapter { name: "the-json-language-server", @@ -716,7 +716,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: "the-language-server", @@ -1125,7 +1125,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { disk_based_diagnostics_progress_token: Some(progress_token.into()), @@ -1247,7 +1247,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: "the-language-server", @@ -1324,8 +1324,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = - language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default()); let buffer = project .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) @@ -1404,8 +1403,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = - language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default()); let buffer = project .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) @@ -1445,7 +1443,7 @@ async fn test_cancel_language_server_work(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: "the-language-server", @@ -1506,14 +1504,14 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { let project = Project::test(fs, ["/dir".as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); - let mut fake_rust_servers = language_registry.register_fake_lsp_adapter( + let mut fake_rust_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { name: "rust-lsp", ..Default::default() }, ); - let mut fake_js_servers = language_registry.register_fake_lsp_adapter( + let mut fake_js_servers = language_registry.register_fake_lsp( "JavaScript", FakeLspAdapter { name: "js-lsp", @@ -1627,7 +1625,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { disk_based_diagnostics_sources: vec!["disk".into()], @@ -2049,8 +2047,7 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = - language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default()); let buffer = project .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) @@ -2421,8 +2418,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = - language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default()); let buffer = project .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx)) @@ -2515,7 +2511,7 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); - let mut fake_language_servers = language_registry.register_fake_lsp_adapter( + let mut fake_language_servers = language_registry.register_fake_lsp( "TypeScript", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -2607,7 +2603,7 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); - let mut fake_language_servers = language_registry.register_fake_lsp_adapter( + let mut fake_language_servers = language_registry.register_fake_lsp( "TypeScript", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -2668,7 +2664,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); - let mut fake_language_servers = language_registry.register_fake_lsp_adapter( + let mut fake_language_servers = language_registry.register_fake_lsp( "TypeScript", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -3310,7 +3306,10 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { assert!(buffer.is_dirty()); assert_eq!( *events.lock(), - &[language::Event::Edited, language::Event::DirtyChanged] + &[ + language::BufferEvent::Edited, + language::BufferEvent::DirtyChanged + ] ); events.lock().clear(); buffer.did_save(buffer.version(), buffer.file().unwrap().mtime(), cx); @@ -3319,7 +3318,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { // after saving, the buffer is not dirty, and emits a saved event. buffer1.update(cx, |buffer, cx| { assert!(!buffer.is_dirty()); - assert_eq!(*events.lock(), &[language::Event::Saved]); + assert_eq!(*events.lock(), &[language::BufferEvent::Saved]); events.lock().clear(); buffer.edit([(1..1, "B")], None, cx); @@ -3333,9 +3332,9 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { assert_eq!( *events.lock(), &[ - language::Event::Edited, - language::Event::DirtyChanged, - language::Event::Edited, + language::BufferEvent::Edited, + language::BufferEvent::DirtyChanged, + language::BufferEvent::Edited, ], ); events.lock().clear(); @@ -3349,7 +3348,10 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { assert_eq!( *events.lock(), - &[language::Event::Edited, language::Event::DirtyChanged] + &[ + language::BufferEvent::Edited, + language::BufferEvent::DirtyChanged + ] ); // When a file is deleted, the buffer is considered dirty. @@ -3374,8 +3376,8 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { assert_eq!( *events.lock(), &[ - language::Event::DirtyChanged, - language::Event::FileHandleChanged + language::BufferEvent::DirtyChanged, + language::BufferEvent::FileHandleChanged ] ); @@ -3401,7 +3403,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { .await .unwrap(); cx.executor().run_until_parked(); - assert_eq!(*events.lock(), &[language::Event::FileHandleChanged]); + assert_eq!(*events.lock(), &[language::BufferEvent::FileHandleChanged]); cx.update(|cx| assert!(buffer3.read(cx).is_dirty())); } @@ -3809,7 +3811,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); - let mut fake_servers = language_registry.register_fake_lsp_adapter( + let mut fake_servers = language_registry.register_fake_lsp( "Rust", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -4696,50 +4698,52 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) { "ESLintServer", "NoHoverCapabilitiesServer", ]; - let mut fake_tsx_language_servers = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[0], - capabilities: lsp::ServerCapabilities { - hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + let mut language_servers = [ + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[0], + capabilities: lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _a = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[1], - capabilities: lsp::ServerCapabilities { - hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + ), + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[1], + capabilities: lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _b = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[2], - capabilities: lsp::ServerCapabilities { - hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + ), + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[2], + capabilities: lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _c = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[3], - capabilities: lsp::ServerCapabilities { - hover_provider: None, - ..lsp::ServerCapabilities::default() + ), + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[3], + capabilities: lsp::ServerCapabilities { + hover_provider: None, + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); + ), + ]; let buffer = project .update(cx, |p, cx| p.open_local_buffer("/dir/a.tsx", cx)) @@ -4749,7 +4753,7 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) { let mut servers_with_hover_requests = HashMap::default(); for i in 0..language_server_names.len() { - let new_server = fake_tsx_language_servers.next().await.unwrap_or_else(|| { + let new_server = language_servers[i].next().await.unwrap_or_else(|| { panic!( "Failed to get language server #{i} with name {}", &language_server_names[i] @@ -4840,7 +4844,7 @@ async fn test_hovers_with_empty_parts(cx: &mut gpui::TestAppContext) { let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); - let mut fake_language_servers = language_registry.register_fake_lsp_adapter( + let mut fake_language_servers = language_registry.register_fake_lsp( "TypeScript", FakeLspAdapter { capabilities: lsp::ServerCapabilities { @@ -4916,50 +4920,53 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) { "ESLintServer", "NoActionsCapabilitiesServer", ]; - let mut fake_tsx_language_servers = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[0], - capabilities: lsp::ServerCapabilities { - code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + + let mut language_server_rxs = [ + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[0], + capabilities: lsp::ServerCapabilities { + code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _a = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[1], - capabilities: lsp::ServerCapabilities { - code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + ), + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[1], + capabilities: lsp::ServerCapabilities { + code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _b = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[2], - capabilities: lsp::ServerCapabilities { - code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)), - ..lsp::ServerCapabilities::default() + ), + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[2], + capabilities: lsp::ServerCapabilities { + code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)), + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); - let _c = language_registry.register_fake_lsp_adapter( - "tsx", - FakeLspAdapter { - name: language_server_names[3], - capabilities: lsp::ServerCapabilities { - code_action_provider: None, - ..lsp::ServerCapabilities::default() + ), + language_registry.register_fake_lsp( + "tsx", + FakeLspAdapter { + name: language_server_names[3], + capabilities: lsp::ServerCapabilities { + code_action_provider: None, + ..lsp::ServerCapabilities::default() + }, + ..FakeLspAdapter::default() }, - ..FakeLspAdapter::default() - }, - ); + ), + ]; let buffer = project .update(cx, |p, cx| p.open_local_buffer("/dir/a.tsx", cx)) @@ -4969,13 +4976,14 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) { let mut servers_with_actions_requests = HashMap::default(); for i in 0..language_server_names.len() { - let new_server = fake_tsx_language_servers.next().await.unwrap_or_else(|| { + let new_server = language_server_rxs[i].next().await.unwrap_or_else(|| { panic!( "Failed to get language server #{i} with name {}", &language_server_names[i] ) }); let new_server_name = new_server.server.name(); + assert!( !servers_with_actions_requests.contains_key(new_server_name), "Unexpected: initialized server with the same name twice. Name: `{new_server_name}`" @@ -5023,6 +5031,8 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) { let code_actions_task = project.update(cx, |project, cx| { project.code_actions(&buffer, 0..buffer.read(cx).len(), cx) }); + + // cx.run_until_parked(); let _: Vec<()> = futures::future::join_all(servers_with_actions_requests.into_values().map( |mut code_actions_request| async move { code_actions_request diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 0cb7ef6c71..80cf90bf9e 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -288,7 +288,7 @@ mod tests { None, ))); let mut fake_servers = - language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default()); + language_registry.register_fake_lsp("Rust", FakeLspAdapter::default()); let _buffer = project .update(cx, |project, cx| { diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index f59e8146b6..a10b3798a4 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -497,6 +497,7 @@ message ShareProject { uint64 room_id = 1; repeated WorktreeMetadata worktrees = 2; optional uint64 dev_server_project_id = 3; + bool is_ssh_project = 4; } message ShareProjectResponse { diff --git a/crates/remote_server/Cargo.toml b/crates/remote_server/Cargo.toml index 9e9a3fdc42..ad5ffbcbfa 100644 --- a/crates/remote_server/Cargo.toml +++ b/crates/remote_server/Cargo.toml @@ -46,7 +46,9 @@ gpui = { workspace = true, features = ["test-support"] } http_client = { workspace = true, features = ["test-support"] } language = { workspace = true, features = ["test-support"] } node_runtime = { workspace = true, features = ["test-support"] } +project = { workspace = true, features = ["test-support"] } remote = { workspace = true, features = ["test-support"] } +lsp = { workspace = true, features=["test-support"] } serde_json.workspace = true diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index 08723daa01..d85e3a184d 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -1,10 +1,13 @@ use anyhow::{anyhow, Result}; use fs::Fs; use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext}; -use language::LanguageRegistry; +use language::{proto::serialize_operation, Buffer, BufferEvent, LanguageRegistry}; use project::{ - buffer_store::BufferStore, project_settings::SettingsObserver, search::SearchQuery, - worktree_store::WorktreeStore, LspStore, ProjectPath, WorktreeId, + buffer_store::{BufferStore, BufferStoreEvent}, + project_settings::SettingsObserver, + search::SearchQuery, + worktree_store::WorktreeStore, + LspStore, ProjectPath, WorktreeId, }; use remote::SshSession; use rpc::{ @@ -26,6 +29,7 @@ pub struct HeadlessProject { pub lsp_store: Model, pub settings_observer: Model, pub next_entry_id: Arc, + pub languages: Arc, } impl HeadlessProject { @@ -60,7 +64,7 @@ impl HeadlessProject { buffer_store.clone(), worktree_store.clone(), environment, - languages, + languages.clone(), None, fs.clone(), cx, @@ -69,6 +73,17 @@ impl HeadlessProject { lsp_store }); + cx.subscribe( + &buffer_store, + |_this, _buffer_store, event, cx| match event { + BufferStoreEvent::BufferAdded(buffer) => { + cx.subscribe(buffer, Self::on_buffer_event).detach(); + } + _ => {} + }, + ) + .detach(); + let client: AnyProtoClient = session.clone().into(); session.subscribe_to_entity(SSH_PROJECT_ID, &worktree_store); @@ -103,6 +118,26 @@ impl HeadlessProject { buffer_store, lsp_store, next_entry_id: Default::default(), + languages, + } + } + + fn on_buffer_event( + &mut self, + buffer: Model, + event: &BufferEvent, + cx: &mut ModelContext, + ) { + match event { + BufferEvent::Operation(op) => cx + .background_executor() + .spawn(self.session.request(proto::UpdateBuffer { + project_id: SSH_PROJECT_ID, + buffer_id: buffer.read(cx).remote_id().to_proto(), + operations: vec![serialize_operation(op)], + })) + .detach(), + _ => {} } } diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index 67a2f0b57d..b7fc56d3c6 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -6,8 +6,9 @@ use gpui::{Context, Model, TestAppContext}; use http_client::FakeHttpClient; use language::{ language_settings::{all_language_settings, AllLanguageSettings}, - Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, + Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LanguageServerName, }; +use lsp::{CompletionContext, CompletionResponse, CompletionTriggerKind}; use node_runtime::FakeNodeRuntime; use project::{ search::{SearchQuery, SearchResult}, @@ -317,6 +318,15 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext }, ) }); + + let mut fake_lsp = server_cx.update(|cx| { + headless.read(cx).languages.register_fake_language_server( + LanguageServerName("rust-analyzer".into()), + Default::default(), + None, + ) + }); + cx.run_until_parked(); let worktree_id = project @@ -339,6 +349,8 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext .unwrap(); cx.run_until_parked(); + let fake_lsp = fake_lsp.next().await.unwrap(); + cx.read(|cx| { let file = buffer.read(cx).file(); assert_eq!( @@ -370,6 +382,62 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext let lsp_store = headless.read(cx).lsp_store.read(cx); assert_eq!(lsp_store.as_local().unwrap().language_servers.len(), 1); }); + + fake_lsp.handle_request::(|_, _| async move { + Ok(Some(CompletionResponse::Array(vec![lsp::CompletionItem { + label: "boop".to_string(), + ..Default::default() + }]))) + }); + + let result = project + .update(cx, |project, cx| { + project.completions( + &buffer, + 0, + CompletionContext { + trigger_kind: CompletionTriggerKind::INVOKED, + trigger_character: None, + }, + cx, + ) + }) + .await + .unwrap(); + + assert_eq!( + result.into_iter().map(|c| c.label.text).collect::>(), + vec!["boop".to_string()] + ); + + fake_lsp.handle_request::(|_, _| async move { + Ok(Some(lsp::WorkspaceEdit { + changes: Some( + [( + lsp::Url::from_file_path("/code/project1/src/lib.rs").unwrap(), + vec![lsp::TextEdit::new( + lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 6)), + "two".to_string(), + )], + )] + .into_iter() + .collect(), + ), + ..Default::default() + })) + }); + + project + .update(cx, |project, cx| { + project.perform_rename(buffer.clone(), 3, "two".to_string(), true, cx) + }) + .await + .unwrap(); + + cx.run_until_parked(); + buffer.update(cx, |buffer, _| { + assert_eq!(buffer.text(), "fn two() -> usize { 1 }") + }) } fn init_logger() {