diff --git a/Cargo.lock b/Cargo.lock index b0a7d6988c..530a2aff6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6256,6 +6256,7 @@ dependencies = [ "libc", "log", "log-panics", + "lsp", "num_cpus", "parking_lot", "people_panel", diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index 81c2d2af27..2139b5560e 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -982,7 +982,8 @@ mod tests { }, editor::{Editor, EditorSettings, Input}, fs::{FakeFs, Fs as _}, - language::LanguageRegistry, + language::{Diagnostic, LanguageRegistry, Point}, + lsp, people_panel::JoinWorktree, project::{ProjectPath, Worktree}, workspace::{Workspace, WorkspaceParams}, @@ -1602,6 +1603,114 @@ mod tests { .await; } + #[gpui::test] + async fn test_collaborating_with_diagnostics( + mut cx_a: TestAppContext, + mut cx_b: TestAppContext, + ) { + cx_a.foreground().forbid_parking(); + let lang_registry = Arc::new(LanguageRegistry::new()); + + let (language_server, mut fake_lsp) = lsp::LanguageServer::fake(cx_a.background()).await; + + // Connect to a server as 2 clients. + let mut server = TestServer::start().await; + let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; + let (client_b, _) = server.create_client(&mut cx_a, "user_b").await; + + // Share a local worktree as client A + let fs = Arc::new(FakeFs::new()); + fs.insert_tree( + "/a", + json!({ + ".zed.toml": r#"collaborators = ["user_b"]"#, + "a.txt": "one two three", + "b.txt": "b-contents", + }), + ) + .await; + let worktree_a = Worktree::open_local( + client_a.clone(), + "/a".as_ref(), + fs, + lang_registry.clone(), + Some(language_server), + &mut cx_a.to_async(), + ) + .await + .unwrap(); + worktree_a + .read_with(&cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) + .await; + let worktree_id = worktree_a + .update(&mut cx_a, |tree, cx| tree.as_local_mut().unwrap().share(cx)) + .await + .unwrap(); + + // Simulate a language server reporting errors for a file. + fake_lsp + .notify::(lsp::PublishDiagnosticsParams { + uri: lsp::Url::from_file_path("/a/a.txt").unwrap(), + version: None, + diagnostics: vec![ + lsp::Diagnostic { + severity: Some(lsp::DiagnosticSeverity::ERROR), + range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 3)), + message: "message 1".to_string(), + ..Default::default() + }, + lsp::Diagnostic { + severity: Some(lsp::DiagnosticSeverity::WARNING), + range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 13)), + message: "message 2".to_string(), + ..Default::default() + }, + ], + }) + .await; + + // Join the worktree as client B. + let worktree_b = Worktree::open_remote( + client_b.clone(), + worktree_id, + lang_registry.clone(), + &mut cx_b.to_async(), + ) + .await + .unwrap(); + + // Open the file with the errors. + let buffer_b = cx_b + .background() + .spawn(worktree_b.update(&mut cx_b, |worktree, cx| worktree.open_buffer("a.txt", cx))) + .await + .unwrap(); + + buffer_b.read_with(&cx_b, |buffer, _| { + assert_eq!( + buffer + .diagnostics_in_range(0..buffer.len()) + .collect::>(), + &[ + ( + Point::new(0, 0)..Point::new(0, 3), + &Diagnostic { + message: "message 1".to_string(), + severity: lsp::DiagnosticSeverity::ERROR, + } + ), + ( + Point { row: 0, column: 8 }..Point { row: 0, column: 13 }, + &Diagnostic { + severity: lsp::DiagnosticSeverity::WARNING, + message: "message 2".to_string() + } + ) + ] + ); + }); + } + #[gpui::test] async fn test_basic_chat(mut cx_a: TestAppContext, mut cx_b: TestAppContext) { cx_a.foreground().forbid_parking(); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 1a536ef73f..3d454c89a7 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -20,6 +20,7 @@ test-support = [ "editor/test-support", "gpui/test-support", "language/test-support", + "lsp/test-support", "project/test-support", "rpc/test-support", "tempdir", @@ -37,6 +38,7 @@ editor = { path = "../editor" } file_finder = { path = "../file_finder" } gpui = { path = "../gpui" } language = { path = "../language" } +lsp = { path = "../lsp" } people_panel = { path = "../people_panel" } project = { path = "../project" } project_panel = { path = "../project_panel" } @@ -90,6 +92,7 @@ buffer = { path = "../buffer", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } +lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } diff --git a/crates/zed/src/language.rs b/crates/zed/src/language.rs index a82f7a2cbb..2c60ddd92c 100644 --- a/crates/zed/src/language.rs +++ b/crates/zed/src/language.rs @@ -1,4 +1,4 @@ -pub use language::{Language, LanguageRegistry}; +pub use language::{Buffer, Diagnostic, Language, LanguageRegistry, Point}; use rust_embed::RustEmbed; use std::borrow::Cow; use std::{str, sync::Arc}; diff --git a/crates/zed/src/lib.rs b/crates/zed/src/lib.rs index cec9e29aa8..5f5a4b17b1 100644 --- a/crates/zed/src/lib.rs +++ b/crates/zed/src/lib.rs @@ -15,6 +15,7 @@ use gpui::{ platform::WindowOptions, ModelHandle, MutableAppContext, PathPromptOptions, Task, ViewContext, }; +pub use lsp; use parking_lot::Mutex; pub use people_panel; use people_panel::PeoplePanel;