project_tests.rs

   1// use crate::{Event, *};
   2// use fs::FakeFs;
   3// use futures::{future, StreamExt};
   4// use gpui::AppContext;
   5// use language::{
   6//     language_settings::{AllLanguageSettings, LanguageSettingsContent},
   7//     tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,
   8//     LineEnding, OffsetRangeExt, Point, ToPoint,
   9// };
  10// use lsp::Url;
  11// use parking_lot::Mutex;
  12// use pretty_assertions::assert_eq;
  13// use serde_json::json;
  14// use std::{os, task::Poll};
  15// use unindent::Unindent as _;
  16// use util::{assert_set_eq, paths::PathMatcher, test::temp_tree};
  17
  18// #[gpui::test]
  19// async fn test_block_via_channel(cx: &mut gpui2::TestAppContext) {
  20//     cx.executor().allow_parking();
  21// }
  22
  23// #[cfg(test)]
  24// #[ctor::ctor]
  25// fn init_logger() {
  26//     if std::env::var("RUST_LOG").is_ok() {
  27//         env_logger::init();
  28//     }
  29// }
  30
  31// #[gpui::test]
  32// async fn test_symlinks(cx: &mut gpui::TestAppContext) {
  33//     init_test(cx);
  34//     cx.foreground().allow_parking();
  35
  36//     let dir = temp_tree(json!({
  37//         "root": {
  38//             "apple": "",
  39//             "banana": {
  40//                 "carrot": {
  41//                     "date": "",
  42//                     "endive": "",
  43//                 }
  44//             },
  45//             "fennel": {
  46//                 "grape": "",
  47//             }
  48//         }
  49//     }));
  50
  51//     let root_link_path = dir.path().join("root_link");
  52//     unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
  53//     unix::fs::symlink(
  54//         &dir.path().join("root/fennel"),
  55//         &dir.path().join("root/finnochio"),
  56//     )
  57//     .unwrap();
  58
  59//     let project = Project::test(Arc::new(RealFs), [root_link_path.as_ref()], cx).await;
  60//     project.read_with(cx, |project, cx| {
  61//         let tree = project.worktrees(cx).next().unwrap().read(cx);
  62//         assert_eq!(tree.file_count(), 5);
  63//         assert_eq!(
  64//             tree.inode_for_path("fennel/grape"),
  65//             tree.inode_for_path("finnochio/grape")
  66//         );
  67//     });
  68// }
  69
  70// #[gpui::test]
  71// async fn test_managing_project_specific_settings(
  72//     deterministic: Arc<Deterministic>,
  73//     cx: &mut gpui::TestAppContext,
  74// ) {
  75//     init_test(cx);
  76
  77//     let fs = FakeFs::new(cx.background());
  78//     fs.insert_tree(
  79//         "/the-root",
  80//         json!({
  81//             ".zed": {
  82//                 "settings.json": r#"{ "tab_size": 8 }"#
  83//             },
  84//             "a": {
  85//                 "a.rs": "fn a() {\n    A\n}"
  86//             },
  87//             "b": {
  88//                 ".zed": {
  89//                     "settings.json": r#"{ "tab_size": 2 }"#
  90//                 },
  91//                 "b.rs": "fn b() {\n  B\n}"
  92//             }
  93//         }),
  94//     )
  95//     .await;
  96
  97//     let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
  98//     let worktree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap());
  99
 100//     deterministic.run_until_parked();
 101//     cx.read(|cx| {
 102//         let tree = worktree.read(cx);
 103
 104//         let settings_a = language_settings(
 105//             None,
 106//             Some(
 107//                 &(File::for_entry(
 108//                     tree.entry_for_path("a/a.rs").unwrap().clone(),
 109//                     worktree.clone(),
 110//                 ) as _),
 111//             ),
 112//             cx,
 113//         );
 114//         let settings_b = language_settings(
 115//             None,
 116//             Some(
 117//                 &(File::for_entry(
 118//                     tree.entry_for_path("b/b.rs").unwrap().clone(),
 119//                     worktree.clone(),
 120//                 ) as _),
 121//             ),
 122//             cx,
 123//         );
 124
 125//         assert_eq!(settings_a.tab_size.get(), 8);
 126//         assert_eq!(settings_b.tab_size.get(), 2);
 127//     });
 128// }
 129
 130// #[gpui::test]
 131// async fn test_managing_language_servers(
 132//     deterministic: Arc<Deterministic>,
 133//     cx: &mut gpui::TestAppContext,
 134// ) {
 135//     init_test(cx);
 136
 137//     let mut rust_language = Language::new(
 138//         LanguageConfig {
 139//             name: "Rust".into(),
 140//             path_suffixes: vec!["rs".to_string()],
 141//             ..Default::default()
 142//         },
 143//         Some(tree_sitter_rust::language()),
 144//     );
 145//     let mut json_language = Language::new(
 146//         LanguageConfig {
 147//             name: "JSON".into(),
 148//             path_suffixes: vec!["json".to_string()],
 149//             ..Default::default()
 150//         },
 151//         None,
 152//     );
 153//     let mut fake_rust_servers = rust_language
 154//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
 155//             name: "the-rust-language-server",
 156//             capabilities: lsp::ServerCapabilities {
 157//                 completion_provider: Some(lsp::CompletionOptions {
 158//                     trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
 159//                     ..Default::default()
 160//                 }),
 161//                 ..Default::default()
 162//             },
 163//             ..Default::default()
 164//         }))
 165//         .await;
 166//     let mut fake_json_servers = json_language
 167//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
 168//             name: "the-json-language-server",
 169//             capabilities: lsp::ServerCapabilities {
 170//                 completion_provider: Some(lsp::CompletionOptions {
 171//                     trigger_characters: Some(vec![":".to_string()]),
 172//                     ..Default::default()
 173//                 }),
 174//                 ..Default::default()
 175//             },
 176//             ..Default::default()
 177//         }))
 178//         .await;
 179
 180//     let fs = FakeFs::new(cx.background());
 181//     fs.insert_tree(
 182//         "/the-root",
 183//         json!({
 184//             "test.rs": "const A: i32 = 1;",
 185//             "test2.rs": "",
 186//             "Cargo.toml": "a = 1",
 187//             "package.json": "{\"a\": 1}",
 188//         }),
 189//     )
 190//     .await;
 191
 192//     let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
 193
 194//     // Open a buffer without an associated language server.
 195//     let toml_buffer = project
 196//         .update(cx, |project, cx| {
 197//             project.open_local_buffer("/the-root/Cargo.toml", cx)
 198//         })
 199//         .await
 200//         .unwrap();
 201
 202//     // Open a buffer with an associated language server before the language for it has been loaded.
 203//     let rust_buffer = project
 204//         .update(cx, |project, cx| {
 205//             project.open_local_buffer("/the-root/test.rs", cx)
 206//         })
 207//         .await
 208//         .unwrap();
 209//     rust_buffer.read_with(cx, |buffer, _| {
 210//         assert_eq!(buffer.language().map(|l| l.name()), None);
 211//     });
 212
 213//     // Now we add the languages to the project, and ensure they get assigned to all
 214//     // the relevant open buffers.
 215//     project.update(cx, |project, _| {
 216//         project.languages.add(Arc::new(json_language));
 217//         project.languages.add(Arc::new(rust_language));
 218//     });
 219//     deterministic.run_until_parked();
 220//     rust_buffer.read_with(cx, |buffer, _| {
 221//         assert_eq!(buffer.language().map(|l| l.name()), Some("Rust".into()));
 222//     });
 223
 224//     // A server is started up, and it is notified about Rust files.
 225//     let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
 226//     assert_eq!(
 227//         fake_rust_server
 228//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 229//             .await
 230//             .text_document,
 231//         lsp2::TextDocumentItem {
 232//             uri: lsp2::Url::from_file_path("/the-root/test.rs").unwrap(),
 233//             version: 0,
 234//             text: "const A: i32 = 1;".to_string(),
 235//             language_id: Default::default()
 236//         }
 237//     );
 238
 239//     // The buffer is configured based on the language server's capabilities.
 240//     rust_buffer.read_with(cx, |buffer, _| {
 241//         assert_eq!(
 242//             buffer.completion_triggers(),
 243//             &[".".to_string(), "::".to_string()]
 244//         );
 245//     });
 246//     toml_buffer.read_with(cx, |buffer, _| {
 247//         assert!(buffer.completion_triggers().is_empty());
 248//     });
 249
 250//     // Edit a buffer. The changes are reported to the language server.
 251//     rust_buffer.update(cx, |buffer, cx| buffer.edit([(16..16, "2")], None, cx));
 252//     assert_eq!(
 253//         fake_rust_server
 254//             .receive_notification::<lsp2::notification::DidChangeTextDocument>()
 255//             .await
 256//             .text_document,
 257//         lsp2::VersionedTextDocumentIdentifier::new(
 258//             lsp2::Url::from_file_path("/the-root/test.rs").unwrap(),
 259//             1
 260//         )
 261//     );
 262
 263//     // Open a third buffer with a different associated language server.
 264//     let json_buffer = project
 265//         .update(cx, |project, cx| {
 266//             project.open_local_buffer("/the-root/package.json", cx)
 267//         })
 268//         .await
 269//         .unwrap();
 270
 271//     // A json language server is started up and is only notified about the json buffer.
 272//     let mut fake_json_server = fake_json_servers.next().await.unwrap();
 273//     assert_eq!(
 274//         fake_json_server
 275//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 276//             .await
 277//             .text_document,
 278//         lsp2::TextDocumentItem {
 279//             uri: lsp2::Url::from_file_path("/the-root/package.json").unwrap(),
 280//             version: 0,
 281//             text: "{\"a\": 1}".to_string(),
 282//             language_id: Default::default()
 283//         }
 284//     );
 285
 286//     // This buffer is configured based on the second language server's
 287//     // capabilities.
 288//     json_buffer.read_with(cx, |buffer, _| {
 289//         assert_eq!(buffer.completion_triggers(), &[":".to_string()]);
 290//     });
 291
 292//     // When opening another buffer whose language server is already running,
 293//     // it is also configured based on the existing language server's capabilities.
 294//     let rust_buffer2 = project
 295//         .update(cx, |project, cx| {
 296//             project.open_local_buffer("/the-root/test2.rs", cx)
 297//         })
 298//         .await
 299//         .unwrap();
 300//     rust_buffer2.read_with(cx, |buffer, _| {
 301//         assert_eq!(
 302//             buffer.completion_triggers(),
 303//             &[".".to_string(), "::".to_string()]
 304//         );
 305//     });
 306
 307//     // Changes are reported only to servers matching the buffer's language.
 308//     toml_buffer.update(cx, |buffer, cx| buffer.edit([(5..5, "23")], None, cx));
 309//     rust_buffer2.update(cx, |buffer, cx| {
 310//         buffer.edit([(0..0, "let x = 1;")], None, cx)
 311//     });
 312//     assert_eq!(
 313//         fake_rust_server
 314//             .receive_notification::<lsp2::notification::DidChangeTextDocument>()
 315//             .await
 316//             .text_document,
 317//         lsp2::VersionedTextDocumentIdentifier::new(
 318//             lsp2::Url::from_file_path("/the-root/test2.rs").unwrap(),
 319//             1
 320//         )
 321//     );
 322
 323//     // Save notifications are reported to all servers.
 324//     project
 325//         .update(cx, |project, cx| project.save_buffer(toml_buffer, cx))
 326//         .await
 327//         .unwrap();
 328//     assert_eq!(
 329//         fake_rust_server
 330//             .receive_notification::<lsp2::notification::DidSaveTextDocument>()
 331//             .await
 332//             .text_document,
 333//         lsp2::TextDocumentIdentifier::new(
 334//             lsp2::Url::from_file_path("/the-root/Cargo.toml").unwrap()
 335//         )
 336//     );
 337//     assert_eq!(
 338//         fake_json_server
 339//             .receive_notification::<lsp2::notification::DidSaveTextDocument>()
 340//             .await
 341//             .text_document,
 342//         lsp2::TextDocumentIdentifier::new(
 343//             lsp2::Url::from_file_path("/the-root/Cargo.toml").unwrap()
 344//         )
 345//     );
 346
 347//     // Renames are reported only to servers matching the buffer's language.
 348//     fs.rename(
 349//         Path::new("/the-root/test2.rs"),
 350//         Path::new("/the-root/test3.rs"),
 351//         Default::default(),
 352//     )
 353//     .await
 354//     .unwrap();
 355//     assert_eq!(
 356//         fake_rust_server
 357//             .receive_notification::<lsp2::notification::DidCloseTextDocument>()
 358//             .await
 359//             .text_document,
 360//         lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path("/the-root/test2.rs").unwrap()),
 361//     );
 362//     assert_eq!(
 363//         fake_rust_server
 364//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 365//             .await
 366//             .text_document,
 367//         lsp2::TextDocumentItem {
 368//             uri: lsp2::Url::from_file_path("/the-root/test3.rs").unwrap(),
 369//             version: 0,
 370//             text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
 371//             language_id: Default::default()
 372//         },
 373//     );
 374
 375//     rust_buffer2.update(cx, |buffer, cx| {
 376//         buffer.update_diagnostics(
 377//             LanguageServerId(0),
 378//             DiagnosticSet::from_sorted_entries(
 379//                 vec![DiagnosticEntry {
 380//                     diagnostic: Default::default(),
 381//                     range: Anchor::MIN..Anchor::MAX,
 382//                 }],
 383//                 &buffer.snapshot(),
 384//             ),
 385//             cx,
 386//         );
 387//         assert_eq!(
 388//             buffer
 389//                 .snapshot()
 390//                 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
 391//                 .count(),
 392//             1
 393//         );
 394//     });
 395
 396//     // When the rename changes the extension of the file, the buffer gets closed on the old
 397//     // language server and gets opened on the new one.
 398//     fs.rename(
 399//         Path::new("/the-root/test3.rs"),
 400//         Path::new("/the-root/test3.json"),
 401//         Default::default(),
 402//     )
 403//     .await
 404//     .unwrap();
 405//     assert_eq!(
 406//         fake_rust_server
 407//             .receive_notification::<lsp2::notification::DidCloseTextDocument>()
 408//             .await
 409//             .text_document,
 410//         lsp2::TextDocumentIdentifier::new(lsp2::Url::from_file_path("/the-root/test3.rs").unwrap(),),
 411//     );
 412//     assert_eq!(
 413//         fake_json_server
 414//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 415//             .await
 416//             .text_document,
 417//         lsp2::TextDocumentItem {
 418//             uri: lsp2::Url::from_file_path("/the-root/test3.json").unwrap(),
 419//             version: 0,
 420//             text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
 421//             language_id: Default::default()
 422//         },
 423//     );
 424
 425//     // We clear the diagnostics, since the language has changed.
 426//     rust_buffer2.read_with(cx, |buffer, _| {
 427//         assert_eq!(
 428//             buffer
 429//                 .snapshot()
 430//                 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
 431//                 .count(),
 432//             0
 433//         );
 434//     });
 435
 436//     // The renamed file's version resets after changing language server.
 437//     rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "// ")], None, cx));
 438//     assert_eq!(
 439//         fake_json_server
 440//             .receive_notification::<lsp2::notification::DidChangeTextDocument>()
 441//             .await
 442//             .text_document,
 443//         lsp2::VersionedTextDocumentIdentifier::new(
 444//             lsp2::Url::from_file_path("/the-root/test3.json").unwrap(),
 445//             1
 446//         )
 447//     );
 448
 449//     // Restart language servers
 450//     project.update(cx, |project, cx| {
 451//         project.restart_language_servers_for_buffers(
 452//             vec![rust_buffer.clone(), json_buffer.clone()],
 453//             cx,
 454//         );
 455//     });
 456
 457//     let mut rust_shutdown_requests = fake_rust_server
 458//         .handle_request::<lsp2::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
 459//     let mut json_shutdown_requests = fake_json_server
 460//         .handle_request::<lsp2::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
 461//     futures::join!(rust_shutdown_requests.next(), json_shutdown_requests.next());
 462
 463//     let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
 464//     let mut fake_json_server = fake_json_servers.next().await.unwrap();
 465
 466//     // Ensure rust document is reopened in new rust language server
 467//     assert_eq!(
 468//         fake_rust_server
 469//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 470//             .await
 471//             .text_document,
 472//         lsp2::TextDocumentItem {
 473//             uri: lsp2::Url::from_file_path("/the-root/test.rs").unwrap(),
 474//             version: 0,
 475//             text: rust_buffer.read_with(cx, |buffer, _| buffer.text()),
 476//             language_id: Default::default()
 477//         }
 478//     );
 479
 480//     // Ensure json documents are reopened in new json language server
 481//     assert_set_eq!(
 482//         [
 483//             fake_json_server
 484//                 .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 485//                 .await
 486//                 .text_document,
 487//             fake_json_server
 488//                 .receive_notification::<lsp2::notification::DidOpenTextDocument>()
 489//                 .await
 490//                 .text_document,
 491//         ],
 492//         [
 493//             lsp2::TextDocumentItem {
 494//                 uri: lsp2::Url::from_file_path("/the-root/package.json").unwrap(),
 495//                 version: 0,
 496//                 text: json_buffer.read_with(cx, |buffer, _| buffer.text()),
 497//                 language_id: Default::default()
 498//             },
 499//             lsp2::TextDocumentItem {
 500//                 uri: lsp2::Url::from_file_path("/the-root/test3.json").unwrap(),
 501//                 version: 0,
 502//                 text: rust_buffer2.read_with(cx, |buffer, _| buffer.text()),
 503//                 language_id: Default::default()
 504//             }
 505//         ]
 506//     );
 507
 508//     // Close notifications are reported only to servers matching the buffer's language.
 509//     cx.update(|_| drop(json_buffer));
 510//     let close_message = lsp2::DidCloseTextDocumentParams {
 511//         text_document: lsp2::TextDocumentIdentifier::new(
 512//             lsp2::Url::from_file_path("/the-root/package.json").unwrap(),
 513//         ),
 514//     };
 515//     assert_eq!(
 516//         fake_json_server
 517//             .receive_notification::<lsp2::notification::DidCloseTextDocument>()
 518//             .await,
 519//         close_message,
 520//     );
 521// }
 522
 523// #[gpui::test]
 524// async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) {
 525//     init_test(cx);
 526
 527//     let mut language = Language::new(
 528//         LanguageConfig {
 529//             name: "Rust".into(),
 530//             path_suffixes: vec!["rs".to_string()],
 531//             ..Default::default()
 532//         },
 533//         Some(tree_sitter_rust::language()),
 534//     );
 535//     let mut fake_servers = language
 536//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
 537//             name: "the-language-server",
 538//             ..Default::default()
 539//         }))
 540//         .await;
 541
 542//     let fs = FakeFs::new(cx.background());
 543//     fs.insert_tree(
 544//         "/the-root",
 545//         json!({
 546//             ".gitignore": "target\n",
 547//             "src": {
 548//                 "a.rs": "",
 549//                 "b.rs": "",
 550//             },
 551//             "target": {
 552//                 "x": {
 553//                     "out": {
 554//                         "x.rs": ""
 555//                     }
 556//                 },
 557//                 "y": {
 558//                     "out": {
 559//                         "y.rs": "",
 560//                     }
 561//                 },
 562//                 "z": {
 563//                     "out": {
 564//                         "z.rs": ""
 565//                     }
 566//                 }
 567//             }
 568//         }),
 569//     )
 570//     .await;
 571
 572//     let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
 573//     project.update(cx, |project, _| {
 574//         project.languages.add(Arc::new(language));
 575//     });
 576//     cx.foreground().run_until_parked();
 577
 578//     // Start the language server by opening a buffer with a compatible file extension.
 579//     let _buffer = project
 580//         .update(cx, |project, cx| {
 581//             project.open_local_buffer("/the-root/src/a.rs", cx)
 582//         })
 583//         .await
 584//         .unwrap();
 585
 586//     // Initially, we don't load ignored files because the language server has not explicitly asked us to watch them.
 587//     project.read_with(cx, |project, cx| {
 588//         let worktree = project.worktrees(cx).next().unwrap();
 589//         assert_eq!(
 590//             worktree
 591//                 .read(cx)
 592//                 .snapshot()
 593//                 .entries(true)
 594//                 .map(|entry| (entry.path.as_ref(), entry.is_ignored))
 595//                 .collect::<Vec<_>>(),
 596//             &[
 597//                 (Path::new(""), false),
 598//                 (Path::new(".gitignore"), false),
 599//                 (Path::new("src"), false),
 600//                 (Path::new("src/a.rs"), false),
 601//                 (Path::new("src/b.rs"), false),
 602//                 (Path::new("target"), true),
 603//             ]
 604//         );
 605//     });
 606
 607//     let prev_read_dir_count = fs.read_dir_call_count();
 608
 609//     // Keep track of the FS events reported to the language server.
 610//     let fake_server = fake_servers.next().await.unwrap();
 611//     let file_changes = Arc::new(Mutex::new(Vec::new()));
 612//     fake_server
 613//         .request::<lsp2::request::RegisterCapability>(lsp2::RegistrationParams {
 614//             registrations: vec![lsp2::Registration {
 615//                 id: Default::default(),
 616//                 method: "workspace/didChangeWatchedFiles".to_string(),
 617//                 register_options: serde_json::to_value(
 618//                     lsp::DidChangeWatchedFilesRegistrationOptions {
 619//                         watchers: vec![
 620//                             lsp2::FileSystemWatcher {
 621//                                 glob_pattern: lsp2::GlobPattern::String(
 622//                                     "/the-root/Cargo.toml".to_string(),
 623//                                 ),
 624//                                 kind: None,
 625//                             },
 626//                             lsp2::FileSystemWatcher {
 627//                                 glob_pattern: lsp2::GlobPattern::String(
 628//                                     "/the-root/src/*.{rs,c}".to_string(),
 629//                                 ),
 630//                                 kind: None,
 631//                             },
 632//                             lsp2::FileSystemWatcher {
 633//                                 glob_pattern: lsp2::GlobPattern::String(
 634//                                     "/the-root/target/y/**/*.rs".to_string(),
 635//                                 ),
 636//                                 kind: None,
 637//                             },
 638//                         ],
 639//                     },
 640//                 )
 641//                 .ok(),
 642//             }],
 643//         })
 644//         .await
 645//         .unwrap();
 646//     fake_server.handle_notification::<lsp2::notification::DidChangeWatchedFiles, _>({
 647//         let file_changes = file_changes.clone();
 648//         move |params, _| {
 649//             let mut file_changes = file_changes.lock();
 650//             file_changes.extend(params.changes);
 651//             file_changes.sort_by(|a, b| a.uri.cmp(&b.uri));
 652//         }
 653//     });
 654
 655//     cx.foreground().run_until_parked();
 656//     assert_eq!(mem::take(&mut *file_changes.lock()), &[]);
 657//     assert_eq!(fs.read_dir_call_count() - prev_read_dir_count, 4);
 658
 659//     // Now the language server has asked us to watch an ignored directory path,
 660//     // so we recursively load it.
 661//     project.read_with(cx, |project, cx| {
 662//         let worktree = project.worktrees(cx).next().unwrap();
 663//         assert_eq!(
 664//             worktree
 665//                 .read(cx)
 666//                 .snapshot()
 667//                 .entries(true)
 668//                 .map(|entry| (entry.path.as_ref(), entry.is_ignored))
 669//                 .collect::<Vec<_>>(),
 670//             &[
 671//                 (Path::new(""), false),
 672//                 (Path::new(".gitignore"), false),
 673//                 (Path::new("src"), false),
 674//                 (Path::new("src/a.rs"), false),
 675//                 (Path::new("src/b.rs"), false),
 676//                 (Path::new("target"), true),
 677//                 (Path::new("target/x"), true),
 678//                 (Path::new("target/y"), true),
 679//                 (Path::new("target/y/out"), true),
 680//                 (Path::new("target/y/out/y.rs"), true),
 681//                 (Path::new("target/z"), true),
 682//             ]
 683//         );
 684//     });
 685
 686//     // Perform some file system mutations, two of which match the watched patterns,
 687//     // and one of which does not.
 688//     fs.create_file("/the-root/src/c.rs".as_ref(), Default::default())
 689//         .await
 690//         .unwrap();
 691//     fs.create_file("/the-root/src/d.txt".as_ref(), Default::default())
 692//         .await
 693//         .unwrap();
 694//     fs.remove_file("/the-root/src/b.rs".as_ref(), Default::default())
 695//         .await
 696//         .unwrap();
 697//     fs.create_file("/the-root/target/x/out/x2.rs".as_ref(), Default::default())
 698//         .await
 699//         .unwrap();
 700//     fs.create_file("/the-root/target/y/out/y2.rs".as_ref(), Default::default())
 701//         .await
 702//         .unwrap();
 703
 704//     // The language server receives events for the FS mutations that match its watch patterns.
 705//     cx.foreground().run_until_parked();
 706//     assert_eq!(
 707//         &*file_changes.lock(),
 708//         &[
 709//             lsp2::FileEvent {
 710//                 uri: lsp2::Url::from_file_path("/the-root/src/b.rs").unwrap(),
 711//                 typ: lsp2::FileChangeType::DELETED,
 712//             },
 713//             lsp2::FileEvent {
 714//                 uri: lsp2::Url::from_file_path("/the-root/src/c.rs").unwrap(),
 715//                 typ: lsp2::FileChangeType::CREATED,
 716//             },
 717//             lsp2::FileEvent {
 718//                 uri: lsp2::Url::from_file_path("/the-root/target/y/out/y2.rs").unwrap(),
 719//                 typ: lsp2::FileChangeType::CREATED,
 720//             },
 721//         ]
 722//     );
 723// }
 724
 725// #[gpui::test]
 726// async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
 727//     init_test(cx);
 728
 729//     let fs = FakeFs::new(cx.background());
 730//     fs.insert_tree(
 731//         "/dir",
 732//         json!({
 733//             "a.rs": "let a = 1;",
 734//             "b.rs": "let b = 2;"
 735//         }),
 736//     )
 737//     .await;
 738
 739//     let project = Project::test(fs, ["/dir/a.rs".as_ref(), "/dir/b.rs".as_ref()], cx).await;
 740
 741//     let buffer_a = project
 742//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
 743//         .await
 744//         .unwrap();
 745//     let buffer_b = project
 746//         .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
 747//         .await
 748//         .unwrap();
 749
 750//     project.update(cx, |project, cx| {
 751//         project
 752//             .update_diagnostics(
 753//                 LanguageServerId(0),
 754//                 lsp::PublishDiagnosticsParams {
 755//                     uri: Url::from_file_path("/dir/a.rs").unwrap(),
 756//                     version: None,
 757//                     diagnostics: vec![lsp2::Diagnostic {
 758//                         range: lsp2::Range::new(
 759//                             lsp2::Position::new(0, 4),
 760//                             lsp2::Position::new(0, 5),
 761//                         ),
 762//                         severity: Some(lsp2::DiagnosticSeverity::ERROR),
 763//                         message: "error 1".to_string(),
 764//                         ..Default::default()
 765//                     }],
 766//                 },
 767//                 &[],
 768//                 cx,
 769//             )
 770//             .unwrap();
 771//         project
 772//             .update_diagnostics(
 773//                 LanguageServerId(0),
 774//                 lsp::PublishDiagnosticsParams {
 775//                     uri: Url::from_file_path("/dir/b.rs").unwrap(),
 776//                     version: None,
 777//                     diagnostics: vec![lsp2::Diagnostic {
 778//                         range: lsp2::Range::new(
 779//                             lsp2::Position::new(0, 4),
 780//                             lsp2::Position::new(0, 5),
 781//                         ),
 782//                         severity: Some(lsp2::DiagnosticSeverity::WARNING),
 783//                         message: "error 2".to_string(),
 784//                         ..Default::default()
 785//                     }],
 786//                 },
 787//                 &[],
 788//                 cx,
 789//             )
 790//             .unwrap();
 791//     });
 792
 793//     buffer_a.read_with(cx, |buffer, _| {
 794//         let chunks = chunks_with_diagnostics(buffer, 0..buffer.len());
 795//         assert_eq!(
 796//             chunks
 797//                 .iter()
 798//                 .map(|(s, d)| (s.as_str(), *d))
 799//                 .collect::<Vec<_>>(),
 800//             &[
 801//                 ("let ", None),
 802//                 ("a", Some(DiagnosticSeverity::ERROR)),
 803//                 (" = 1;", None),
 804//             ]
 805//         );
 806//     });
 807//     buffer_b.read_with(cx, |buffer, _| {
 808//         let chunks = chunks_with_diagnostics(buffer, 0..buffer.len());
 809//         assert_eq!(
 810//             chunks
 811//                 .iter()
 812//                 .map(|(s, d)| (s.as_str(), *d))
 813//                 .collect::<Vec<_>>(),
 814//             &[
 815//                 ("let ", None),
 816//                 ("b", Some(DiagnosticSeverity::WARNING)),
 817//                 (" = 2;", None),
 818//             ]
 819//         );
 820//     });
 821// }
 822
 823// #[gpui::test]
 824// async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
 825//     init_test(cx);
 826
 827//     let fs = FakeFs::new(cx.background());
 828//     fs.insert_tree(
 829//         "/root",
 830//         json!({
 831//             "dir": {
 832//                 "a.rs": "let a = 1;",
 833//             },
 834//             "other.rs": "let b = c;"
 835//         }),
 836//     )
 837//     .await;
 838
 839//     let project = Project::test(fs, ["/root/dir".as_ref()], cx).await;
 840
 841//     let (worktree, _) = project
 842//         .update(cx, |project, cx| {
 843//             project.find_or_create_local_worktree("/root/other.rs", false, cx)
 844//         })
 845//         .await
 846//         .unwrap();
 847//     let worktree_id = worktree.read_with(cx, |tree, _| tree.id());
 848
 849//     project.update(cx, |project, cx| {
 850//         project
 851//             .update_diagnostics(
 852//                 LanguageServerId(0),
 853//                 lsp::PublishDiagnosticsParams {
 854//                     uri: Url::from_file_path("/root/other.rs").unwrap(),
 855//                     version: None,
 856//                     diagnostics: vec![lsp2::Diagnostic {
 857//                         range: lsp2::Range::new(
 858//                             lsp2::Position::new(0, 8),
 859//                             lsp2::Position::new(0, 9),
 860//                         ),
 861//                         severity: Some(lsp2::DiagnosticSeverity::ERROR),
 862//                         message: "unknown variable 'c'".to_string(),
 863//                         ..Default::default()
 864//                     }],
 865//                 },
 866//                 &[],
 867//                 cx,
 868//             )
 869//             .unwrap();
 870//     });
 871
 872//     let buffer = project
 873//         .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx))
 874//         .await
 875//         .unwrap();
 876//     buffer.read_with(cx, |buffer, _| {
 877//         let chunks = chunks_with_diagnostics(buffer, 0..buffer.len());
 878//         assert_eq!(
 879//             chunks
 880//                 .iter()
 881//                 .map(|(s, d)| (s.as_str(), *d))
 882//                 .collect::<Vec<_>>(),
 883//             &[
 884//                 ("let b = ", None),
 885//                 ("c", Some(DiagnosticSeverity::ERROR)),
 886//                 (";", None),
 887//             ]
 888//         );
 889//     });
 890
 891//     project.read_with(cx, |project, cx| {
 892//         assert_eq!(project.diagnostic_summaries(cx).next(), None);
 893//         assert_eq!(project.diagnostic_summary(cx).error_count, 0);
 894//     });
 895// }
 896
 897// #[gpui::test]
 898// async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
 899//     init_test(cx);
 900
 901//     let progress_token = "the-progress-token";
 902//     let mut language = Language::new(
 903//         LanguageConfig {
 904//             name: "Rust".into(),
 905//             path_suffixes: vec!["rs".to_string()],
 906//             ..Default::default()
 907//         },
 908//         Some(tree_sitter_rust::language()),
 909//     );
 910//     let mut fake_servers = language
 911//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
 912//             disk_based_diagnostics_progress_token: Some(progress_token.into()),
 913//             disk_based_diagnostics_sources: vec!["disk".into()],
 914//             ..Default::default()
 915//         }))
 916//         .await;
 917
 918//     let fs = FakeFs::new(cx.background());
 919//     fs.insert_tree(
 920//         "/dir",
 921//         json!({
 922//             "a.rs": "fn a() { A }",
 923//             "b.rs": "const y: i32 = 1",
 924//         }),
 925//     )
 926//     .await;
 927
 928//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
 929//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
 930//     let worktree_id = project.read_with(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
 931
 932//     // Cause worktree to start the fake language server
 933//     let _buffer = project
 934//         .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
 935//         .await
 936//         .unwrap();
 937
 938//     let mut events = subscribe(&project, cx);
 939
 940//     let fake_server = fake_servers.next().await.unwrap();
 941//     assert_eq!(
 942//         events.next().await.unwrap(),
 943//         Event::LanguageServerAdded(LanguageServerId(0)),
 944//     );
 945
 946//     fake_server
 947//         .start_progress(format!("{}/0", progress_token))
 948//         .await;
 949//     assert_eq!(
 950//         events.next().await.unwrap(),
 951//         Event::DiskBasedDiagnosticsStarted {
 952//             language_server_id: LanguageServerId(0),
 953//         }
 954//     );
 955
 956//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
 957//         uri: Url::from_file_path("/dir/a.rs").unwrap(),
 958//         version: None,
 959//         diagnostics: vec![lsp2::Diagnostic {
 960//             range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
 961//             severity: Some(lsp2::DiagnosticSeverity::ERROR),
 962//             message: "undefined variable 'A'".to_string(),
 963//             ..Default::default()
 964//         }],
 965//     });
 966//     assert_eq!(
 967//         events.next().await.unwrap(),
 968//         Event::DiagnosticsUpdated {
 969//             language_server_id: LanguageServerId(0),
 970//             path: (worktree_id, Path::new("a.rs")).into()
 971//         }
 972//     );
 973
 974//     fake_server.end_progress(format!("{}/0", progress_token));
 975//     assert_eq!(
 976//         events.next().await.unwrap(),
 977//         Event::DiskBasedDiagnosticsFinished {
 978//             language_server_id: LanguageServerId(0)
 979//         }
 980//     );
 981
 982//     let buffer = project
 983//         .update(cx, |p, cx| p.open_local_buffer("/dir/a.rs", cx))
 984//         .await
 985//         .unwrap();
 986
 987//     buffer.read_with(cx, |buffer, _| {
 988//         let snapshot = buffer.snapshot();
 989//         let diagnostics = snapshot
 990//             .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
 991//             .collect::<Vec<_>>();
 992//         assert_eq!(
 993//             diagnostics,
 994//             &[DiagnosticEntry {
 995//                 range: Point::new(0, 9)..Point::new(0, 10),
 996//                 diagnostic: Diagnostic {
 997//                     severity: lsp2::DiagnosticSeverity::ERROR,
 998//                     message: "undefined variable 'A'".to_string(),
 999//                     group_id: 0,
1000//                     is_primary: true,
1001//                     ..Default::default()
1002//                 }
1003//             }]
1004//         )
1005//     });
1006
1007//     // Ensure publishing empty diagnostics twice only results in one update event.
1008//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1009//         uri: Url::from_file_path("/dir/a.rs").unwrap(),
1010//         version: None,
1011//         diagnostics: Default::default(),
1012//     });
1013//     assert_eq!(
1014//         events.next().await.unwrap(),
1015//         Event::DiagnosticsUpdated {
1016//             language_server_id: LanguageServerId(0),
1017//             path: (worktree_id, Path::new("a.rs")).into()
1018//         }
1019//     );
1020
1021//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1022//         uri: Url::from_file_path("/dir/a.rs").unwrap(),
1023//         version: None,
1024//         diagnostics: Default::default(),
1025//     });
1026//     cx.foreground().run_until_parked();
1027//     assert_eq!(futures::poll!(events.next()), Poll::Pending);
1028// }
1029
1030// #[gpui::test]
1031// async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppContext) {
1032//     init_test(cx);
1033
1034//     let progress_token = "the-progress-token";
1035//     let mut language = Language::new(
1036//         LanguageConfig {
1037//             path_suffixes: vec!["rs".to_string()],
1038//             ..Default::default()
1039//         },
1040//         None,
1041//     );
1042//     let mut fake_servers = language
1043//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1044//             disk_based_diagnostics_sources: vec!["disk".into()],
1045//             disk_based_diagnostics_progress_token: Some(progress_token.into()),
1046//             ..Default::default()
1047//         }))
1048//         .await;
1049
1050//     let fs = FakeFs::new(cx.background());
1051//     fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
1052
1053//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1054//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
1055
1056//     let buffer = project
1057//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1058//         .await
1059//         .unwrap();
1060
1061//     // Simulate diagnostics starting to update.
1062//     let fake_server = fake_servers.next().await.unwrap();
1063//     fake_server.start_progress(progress_token).await;
1064
1065//     // Restart the server before the diagnostics finish updating.
1066//     project.update(cx, |project, cx| {
1067//         project.restart_language_servers_for_buffers([buffer], cx);
1068//     });
1069//     let mut events = subscribe(&project, cx);
1070
1071//     // Simulate the newly started server sending more diagnostics.
1072//     let fake_server = fake_servers.next().await.unwrap();
1073//     assert_eq!(
1074//         events.next().await.unwrap(),
1075//         Event::LanguageServerAdded(LanguageServerId(1))
1076//     );
1077//     fake_server.start_progress(progress_token).await;
1078//     assert_eq!(
1079//         events.next().await.unwrap(),
1080//         Event::DiskBasedDiagnosticsStarted {
1081//             language_server_id: LanguageServerId(1)
1082//         }
1083//     );
1084//     project.read_with(cx, |project, _| {
1085//         assert_eq!(
1086//             project
1087//                 .language_servers_running_disk_based_diagnostics()
1088//                 .collect::<Vec<_>>(),
1089//             [LanguageServerId(1)]
1090//         );
1091//     });
1092
1093//     // All diagnostics are considered done, despite the old server's diagnostic
1094//     // task never completing.
1095//     fake_server.end_progress(progress_token);
1096//     assert_eq!(
1097//         events.next().await.unwrap(),
1098//         Event::DiskBasedDiagnosticsFinished {
1099//             language_server_id: LanguageServerId(1)
1100//         }
1101//     );
1102//     project.read_with(cx, |project, _| {
1103//         assert_eq!(
1104//             project
1105//                 .language_servers_running_disk_based_diagnostics()
1106//                 .collect::<Vec<_>>(),
1107//             [LanguageServerId(0); 0]
1108//         );
1109//     });
1110// }
1111
1112// #[gpui::test]
1113// async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAppContext) {
1114//     init_test(cx);
1115
1116//     let mut language = Language::new(
1117//         LanguageConfig {
1118//             path_suffixes: vec!["rs".to_string()],
1119//             ..Default::default()
1120//         },
1121//         None,
1122//     );
1123//     let mut fake_servers = language
1124//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1125//             ..Default::default()
1126//         }))
1127//         .await;
1128
1129//     let fs = FakeFs::new(cx.background());
1130//     fs.insert_tree("/dir", json!({ "a.rs": "x" })).await;
1131
1132//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1133//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
1134
1135//     let buffer = project
1136//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1137//         .await
1138//         .unwrap();
1139
1140//     // Publish diagnostics
1141//     let fake_server = fake_servers.next().await.unwrap();
1142//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1143//         uri: Url::from_file_path("/dir/a.rs").unwrap(),
1144//         version: None,
1145//         diagnostics: vec![lsp2::Diagnostic {
1146//             range: lsp2::Range::new(lsp2::Position::new(0, 0), lsp2::Position::new(0, 0)),
1147//             severity: Some(lsp2::DiagnosticSeverity::ERROR),
1148//             message: "the message".to_string(),
1149//             ..Default::default()
1150//         }],
1151//     });
1152
1153//     cx.foreground().run_until_parked();
1154//     buffer.read_with(cx, |buffer, _| {
1155//         assert_eq!(
1156//             buffer
1157//                 .snapshot()
1158//                 .diagnostics_in_range::<_, usize>(0..1, false)
1159//                 .map(|entry| entry.diagnostic.message.clone())
1160//                 .collect::<Vec<_>>(),
1161//             ["the message".to_string()]
1162//         );
1163//     });
1164//     project.read_with(cx, |project, cx| {
1165//         assert_eq!(
1166//             project.diagnostic_summary(cx),
1167//             DiagnosticSummary {
1168//                 error_count: 1,
1169//                 warning_count: 0,
1170//             }
1171//         );
1172//     });
1173
1174//     project.update(cx, |project, cx| {
1175//         project.restart_language_servers_for_buffers([buffer.clone()], cx);
1176//     });
1177
1178//     // The diagnostics are cleared.
1179//     cx.foreground().run_until_parked();
1180//     buffer.read_with(cx, |buffer, _| {
1181//         assert_eq!(
1182//             buffer
1183//                 .snapshot()
1184//                 .diagnostics_in_range::<_, usize>(0..1, false)
1185//                 .map(|entry| entry.diagnostic.message.clone())
1186//                 .collect::<Vec<_>>(),
1187//             Vec::<String>::new(),
1188//         );
1189//     });
1190//     project.read_with(cx, |project, cx| {
1191//         assert_eq!(
1192//             project.diagnostic_summary(cx),
1193//             DiagnosticSummary {
1194//                 error_count: 0,
1195//                 warning_count: 0,
1196//             }
1197//         );
1198//     });
1199// }
1200
1201// #[gpui::test]
1202// async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::TestAppContext) {
1203//     init_test(cx);
1204
1205//     let mut language = Language::new(
1206//         LanguageConfig {
1207//             path_suffixes: vec!["rs".to_string()],
1208//             ..Default::default()
1209//         },
1210//         None,
1211//     );
1212//     let mut fake_servers = language
1213//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1214//             name: "the-lsp",
1215//             ..Default::default()
1216//         }))
1217//         .await;
1218
1219//     let fs = FakeFs::new(cx.background());
1220//     fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
1221
1222//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1223//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
1224
1225//     let buffer = project
1226//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1227//         .await
1228//         .unwrap();
1229
1230//     // Before restarting the server, report diagnostics with an unknown buffer version.
1231//     let fake_server = fake_servers.next().await.unwrap();
1232//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1233//         uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
1234//         version: Some(10000),
1235//         diagnostics: Vec::new(),
1236//     });
1237//     cx.foreground().run_until_parked();
1238
1239//     project.update(cx, |project, cx| {
1240//         project.restart_language_servers_for_buffers([buffer.clone()], cx);
1241//     });
1242//     let mut fake_server = fake_servers.next().await.unwrap();
1243//     let notification = fake_server
1244//         .receive_notification::<lsp2::notification::DidOpenTextDocument>()
1245//         .await
1246//         .text_document;
1247//     assert_eq!(notification.version, 0);
1248// }
1249
1250// #[gpui::test]
1251// async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
1252//     init_test(cx);
1253
1254//     let mut rust = Language::new(
1255//         LanguageConfig {
1256//             name: Arc::from("Rust"),
1257//             path_suffixes: vec!["rs".to_string()],
1258//             ..Default::default()
1259//         },
1260//         None,
1261//     );
1262//     let mut fake_rust_servers = rust
1263//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1264//             name: "rust-lsp",
1265//             ..Default::default()
1266//         }))
1267//         .await;
1268//     let mut js = Language::new(
1269//         LanguageConfig {
1270//             name: Arc::from("JavaScript"),
1271//             path_suffixes: vec!["js".to_string()],
1272//             ..Default::default()
1273//         },
1274//         None,
1275//     );
1276//     let mut fake_js_servers = js
1277//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1278//             name: "js-lsp",
1279//             ..Default::default()
1280//         }))
1281//         .await;
1282
1283//     let fs = FakeFs::new(cx.background());
1284//     fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" }))
1285//         .await;
1286
1287//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1288//     project.update(cx, |project, _| {
1289//         project.languages.add(Arc::new(rust));
1290//         project.languages.add(Arc::new(js));
1291//     });
1292
1293//     let _rs_buffer = project
1294//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1295//         .await
1296//         .unwrap();
1297//     let _js_buffer = project
1298//         .update(cx, |project, cx| project.open_local_buffer("/dir/b.js", cx))
1299//         .await
1300//         .unwrap();
1301
1302//     let mut fake_rust_server_1 = fake_rust_servers.next().await.unwrap();
1303//     assert_eq!(
1304//         fake_rust_server_1
1305//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
1306//             .await
1307//             .text_document
1308//             .uri
1309//             .as_str(),
1310//         "file:///dir/a.rs"
1311//     );
1312
1313//     let mut fake_js_server = fake_js_servers.next().await.unwrap();
1314//     assert_eq!(
1315//         fake_js_server
1316//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
1317//             .await
1318//             .text_document
1319//             .uri
1320//             .as_str(),
1321//         "file:///dir/b.js"
1322//     );
1323
1324//     // Disable Rust language server, ensuring only that server gets stopped.
1325//     cx.update(|cx| {
1326//         cx.update_global(|settings: &mut SettingsStore, cx| {
1327//             settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
1328//                 settings.languages.insert(
1329//                     Arc::from("Rust"),
1330//                     LanguageSettingsContent {
1331//                         enable_language_server: Some(false),
1332//                         ..Default::default()
1333//                     },
1334//                 );
1335//             });
1336//         })
1337//     });
1338//     fake_rust_server_1
1339//         .receive_notification::<lsp2::notification::Exit>()
1340//         .await;
1341
1342//     // Enable Rust and disable JavaScript language servers, ensuring that the
1343//     // former gets started again and that the latter stops.
1344//     cx.update(|cx| {
1345//         cx.update_global(|settings: &mut SettingsStore, cx| {
1346//             settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
1347//                 settings.languages.insert(
1348//                     Arc::from("Rust"),
1349//                     LanguageSettingsContent {
1350//                         enable_language_server: Some(true),
1351//                         ..Default::default()
1352//                     },
1353//                 );
1354//                 settings.languages.insert(
1355//                     Arc::from("JavaScript"),
1356//                     LanguageSettingsContent {
1357//                         enable_language_server: Some(false),
1358//                         ..Default::default()
1359//                     },
1360//                 );
1361//             });
1362//         })
1363//     });
1364//     let mut fake_rust_server_2 = fake_rust_servers.next().await.unwrap();
1365//     assert_eq!(
1366//         fake_rust_server_2
1367//             .receive_notification::<lsp2::notification::DidOpenTextDocument>()
1368//             .await
1369//             .text_document
1370//             .uri
1371//             .as_str(),
1372//         "file:///dir/a.rs"
1373//     );
1374//     fake_js_server
1375//         .receive_notification::<lsp2::notification::Exit>()
1376//         .await;
1377// }
1378
1379// #[gpui::test(iterations = 3)]
1380// async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
1381//     init_test(cx);
1382
1383//     let mut language = Language::new(
1384//         LanguageConfig {
1385//             name: "Rust".into(),
1386//             path_suffixes: vec!["rs".to_string()],
1387//             ..Default::default()
1388//         },
1389//         Some(tree_sitter_rust::language()),
1390//     );
1391//     let mut fake_servers = language
1392//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1393//             disk_based_diagnostics_sources: vec!["disk".into()],
1394//             ..Default::default()
1395//         }))
1396//         .await;
1397
1398//     let text = "
1399//         fn a() { A }
1400//         fn b() { BB }
1401//         fn c() { CCC }
1402//     "
1403//     .unindent();
1404
1405//     let fs = FakeFs::new(cx.background());
1406//     fs.insert_tree("/dir", json!({ "a.rs": text })).await;
1407
1408//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1409//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
1410
1411//     let buffer = project
1412//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1413//         .await
1414//         .unwrap();
1415
1416//     let mut fake_server = fake_servers.next().await.unwrap();
1417//     let open_notification = fake_server
1418//         .receive_notification::<lsp2::notification::DidOpenTextDocument>()
1419//         .await;
1420
1421//     // Edit the buffer, moving the content down
1422//     buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "\n\n")], None, cx));
1423//     let change_notification_1 = fake_server
1424//         .receive_notification::<lsp2::notification::DidChangeTextDocument>()
1425//         .await;
1426//     assert!(change_notification_1.text_document.version > open_notification.text_document.version);
1427
1428//     // Report some diagnostics for the initial version of the buffer
1429//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1430//         uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
1431//         version: Some(open_notification.text_document.version),
1432//         diagnostics: vec![
1433//             lsp2::Diagnostic {
1434//                 range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
1435//                 severity: Some(DiagnosticSeverity::ERROR),
1436//                 message: "undefined variable 'A'".to_string(),
1437//                 source: Some("disk".to_string()),
1438//                 ..Default::default()
1439//             },
1440//             lsp2::Diagnostic {
1441//                 range: lsp2::Range::new(lsp2::Position::new(1, 9), lsp2::Position::new(1, 11)),
1442//                 severity: Some(DiagnosticSeverity::ERROR),
1443//                 message: "undefined variable 'BB'".to_string(),
1444//                 source: Some("disk".to_string()),
1445//                 ..Default::default()
1446//             },
1447//             lsp2::Diagnostic {
1448//                 range: lsp2::Range::new(lsp2::Position::new(2, 9), lsp2::Position::new(2, 12)),
1449//                 severity: Some(DiagnosticSeverity::ERROR),
1450//                 source: Some("disk".to_string()),
1451//                 message: "undefined variable 'CCC'".to_string(),
1452//                 ..Default::default()
1453//             },
1454//         ],
1455//     });
1456
1457//     // The diagnostics have moved down since they were created.
1458//     buffer.next_notification(cx).await;
1459//     cx.foreground().run_until_parked();
1460//     buffer.read_with(cx, |buffer, _| {
1461//         assert_eq!(
1462//             buffer
1463//                 .snapshot()
1464//                 .diagnostics_in_range::<_, Point>(Point::new(3, 0)..Point::new(5, 0), false)
1465//                 .collect::<Vec<_>>(),
1466//             &[
1467//                 DiagnosticEntry {
1468//                     range: Point::new(3, 9)..Point::new(3, 11),
1469//                     diagnostic: Diagnostic {
1470//                         source: Some("disk".into()),
1471//                         severity: DiagnosticSeverity::ERROR,
1472//                         message: "undefined variable 'BB'".to_string(),
1473//                         is_disk_based: true,
1474//                         group_id: 1,
1475//                         is_primary: true,
1476//                         ..Default::default()
1477//                     },
1478//                 },
1479//                 DiagnosticEntry {
1480//                     range: Point::new(4, 9)..Point::new(4, 12),
1481//                     diagnostic: Diagnostic {
1482//                         source: Some("disk".into()),
1483//                         severity: DiagnosticSeverity::ERROR,
1484//                         message: "undefined variable 'CCC'".to_string(),
1485//                         is_disk_based: true,
1486//                         group_id: 2,
1487//                         is_primary: true,
1488//                         ..Default::default()
1489//                     }
1490//                 }
1491//             ]
1492//         );
1493//         assert_eq!(
1494//             chunks_with_diagnostics(buffer, 0..buffer.len()),
1495//             [
1496//                 ("\n\nfn a() { ".to_string(), None),
1497//                 ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
1498//                 (" }\nfn b() { ".to_string(), None),
1499//                 ("BB".to_string(), Some(DiagnosticSeverity::ERROR)),
1500//                 (" }\nfn c() { ".to_string(), None),
1501//                 ("CCC".to_string(), Some(DiagnosticSeverity::ERROR)),
1502//                 (" }\n".to_string(), None),
1503//             ]
1504//         );
1505//         assert_eq!(
1506//             chunks_with_diagnostics(buffer, Point::new(3, 10)..Point::new(4, 11)),
1507//             [
1508//                 ("B".to_string(), Some(DiagnosticSeverity::ERROR)),
1509//                 (" }\nfn c() { ".to_string(), None),
1510//                 ("CC".to_string(), Some(DiagnosticSeverity::ERROR)),
1511//             ]
1512//         );
1513//     });
1514
1515//     // Ensure overlapping diagnostics are highlighted correctly.
1516//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1517//         uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
1518//         version: Some(open_notification.text_document.version),
1519//         diagnostics: vec![
1520//             lsp2::Diagnostic {
1521//                 range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
1522//                 severity: Some(DiagnosticSeverity::ERROR),
1523//                 message: "undefined variable 'A'".to_string(),
1524//                 source: Some("disk".to_string()),
1525//                 ..Default::default()
1526//             },
1527//             lsp2::Diagnostic {
1528//                 range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 12)),
1529//                 severity: Some(DiagnosticSeverity::WARNING),
1530//                 message: "unreachable statement".to_string(),
1531//                 source: Some("disk".to_string()),
1532//                 ..Default::default()
1533//             },
1534//         ],
1535//     });
1536
1537//     buffer.next_notification(cx).await;
1538//     cx.foreground().run_until_parked();
1539//     buffer.read_with(cx, |buffer, _| {
1540//         assert_eq!(
1541//             buffer
1542//                 .snapshot()
1543//                 .diagnostics_in_range::<_, Point>(Point::new(2, 0)..Point::new(3, 0), false)
1544//                 .collect::<Vec<_>>(),
1545//             &[
1546//                 DiagnosticEntry {
1547//                     range: Point::new(2, 9)..Point::new(2, 12),
1548//                     diagnostic: Diagnostic {
1549//                         source: Some("disk".into()),
1550//                         severity: DiagnosticSeverity::WARNING,
1551//                         message: "unreachable statement".to_string(),
1552//                         is_disk_based: true,
1553//                         group_id: 4,
1554//                         is_primary: true,
1555//                         ..Default::default()
1556//                     }
1557//                 },
1558//                 DiagnosticEntry {
1559//                     range: Point::new(2, 9)..Point::new(2, 10),
1560//                     diagnostic: Diagnostic {
1561//                         source: Some("disk".into()),
1562//                         severity: DiagnosticSeverity::ERROR,
1563//                         message: "undefined variable 'A'".to_string(),
1564//                         is_disk_based: true,
1565//                         group_id: 3,
1566//                         is_primary: true,
1567//                         ..Default::default()
1568//                     },
1569//                 }
1570//             ]
1571//         );
1572//         assert_eq!(
1573//             chunks_with_diagnostics(buffer, Point::new(2, 0)..Point::new(3, 0)),
1574//             [
1575//                 ("fn a() { ".to_string(), None),
1576//                 ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
1577//                 (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
1578//                 ("\n".to_string(), None),
1579//             ]
1580//         );
1581//         assert_eq!(
1582//             chunks_with_diagnostics(buffer, Point::new(2, 10)..Point::new(3, 0)),
1583//             [
1584//                 (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
1585//                 ("\n".to_string(), None),
1586//             ]
1587//         );
1588//     });
1589
1590//     // Keep editing the buffer and ensure disk-based diagnostics get translated according to the
1591//     // changes since the last save.
1592//     buffer.update(cx, |buffer, cx| {
1593//         buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "    ")], None, cx);
1594//         buffer.edit(
1595//             [(Point::new(2, 8)..Point::new(2, 10), "(x: usize)")],
1596//             None,
1597//             cx,
1598//         );
1599//         buffer.edit([(Point::new(3, 10)..Point::new(3, 10), "xxx")], None, cx);
1600//     });
1601//     let change_notification_2 = fake_server
1602//         .receive_notification::<lsp2::notification::DidChangeTextDocument>()
1603//         .await;
1604//     assert!(
1605//         change_notification_2.text_document.version > change_notification_1.text_document.version
1606//     );
1607
1608//     // Handle out-of-order diagnostics
1609//     fake_server.notify::<lsp2::notification::PublishDiagnostics>(lsp2::PublishDiagnosticsParams {
1610//         uri: lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
1611//         version: Some(change_notification_2.text_document.version),
1612//         diagnostics: vec![
1613//             lsp2::Diagnostic {
1614//                 range: lsp2::Range::new(lsp2::Position::new(1, 9), lsp2::Position::new(1, 11)),
1615//                 severity: Some(DiagnosticSeverity::ERROR),
1616//                 message: "undefined variable 'BB'".to_string(),
1617//                 source: Some("disk".to_string()),
1618//                 ..Default::default()
1619//             },
1620//             lsp2::Diagnostic {
1621//                 range: lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
1622//                 severity: Some(DiagnosticSeverity::WARNING),
1623//                 message: "undefined variable 'A'".to_string(),
1624//                 source: Some("disk".to_string()),
1625//                 ..Default::default()
1626//             },
1627//         ],
1628//     });
1629
1630//     buffer.next_notification(cx).await;
1631//     cx.foreground().run_until_parked();
1632//     buffer.read_with(cx, |buffer, _| {
1633//         assert_eq!(
1634//             buffer
1635//                 .snapshot()
1636//                 .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
1637//                 .collect::<Vec<_>>(),
1638//             &[
1639//                 DiagnosticEntry {
1640//                     range: Point::new(2, 21)..Point::new(2, 22),
1641//                     diagnostic: Diagnostic {
1642//                         source: Some("disk".into()),
1643//                         severity: DiagnosticSeverity::WARNING,
1644//                         message: "undefined variable 'A'".to_string(),
1645//                         is_disk_based: true,
1646//                         group_id: 6,
1647//                         is_primary: true,
1648//                         ..Default::default()
1649//                     }
1650//                 },
1651//                 DiagnosticEntry {
1652//                     range: Point::new(3, 9)..Point::new(3, 14),
1653//                     diagnostic: Diagnostic {
1654//                         source: Some("disk".into()),
1655//                         severity: DiagnosticSeverity::ERROR,
1656//                         message: "undefined variable 'BB'".to_string(),
1657//                         is_disk_based: true,
1658//                         group_id: 5,
1659//                         is_primary: true,
1660//                         ..Default::default()
1661//                     },
1662//                 }
1663//             ]
1664//         );
1665//     });
1666// }
1667
1668// #[gpui::test]
1669// async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
1670//     init_test(cx);
1671
1672//     let text = concat!(
1673//         "let one = ;\n", //
1674//         "let two = \n",
1675//         "let three = 3;\n",
1676//     );
1677
1678//     let fs = FakeFs::new(cx.background());
1679//     fs.insert_tree("/dir", json!({ "a.rs": text })).await;
1680
1681//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1682//     let buffer = project
1683//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1684//         .await
1685//         .unwrap();
1686
1687//     project.update(cx, |project, cx| {
1688//         project
1689//             .update_buffer_diagnostics(
1690//                 &buffer,
1691//                 LanguageServerId(0),
1692//                 None,
1693//                 vec![
1694//                     DiagnosticEntry {
1695//                         range: Unclipped(PointUtf16::new(0, 10))..Unclipped(PointUtf16::new(0, 10)),
1696//                         diagnostic: Diagnostic {
1697//                             severity: DiagnosticSeverity::ERROR,
1698//                             message: "syntax error 1".to_string(),
1699//                             ..Default::default()
1700//                         },
1701//                     },
1702//                     DiagnosticEntry {
1703//                         range: Unclipped(PointUtf16::new(1, 10))..Unclipped(PointUtf16::new(1, 10)),
1704//                         diagnostic: Diagnostic {
1705//                             severity: DiagnosticSeverity::ERROR,
1706//                             message: "syntax error 2".to_string(),
1707//                             ..Default::default()
1708//                         },
1709//                     },
1710//                 ],
1711//                 cx,
1712//             )
1713//             .unwrap();
1714//     });
1715
1716//     // An empty range is extended forward to include the following character.
1717//     // At the end of a line, an empty range is extended backward to include
1718//     // the preceding character.
1719//     buffer.read_with(cx, |buffer, _| {
1720//         let chunks = chunks_with_diagnostics(buffer, 0..buffer.len());
1721//         assert_eq!(
1722//             chunks
1723//                 .iter()
1724//                 .map(|(s, d)| (s.as_str(), *d))
1725//                 .collect::<Vec<_>>(),
1726//             &[
1727//                 ("let one = ", None),
1728//                 (";", Some(DiagnosticSeverity::ERROR)),
1729//                 ("\nlet two =", None),
1730//                 (" ", Some(DiagnosticSeverity::ERROR)),
1731//                 ("\nlet three = 3;\n", None)
1732//             ]
1733//         );
1734//     });
1735// }
1736
1737// #[gpui::test]
1738// async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppContext) {
1739//     init_test(cx);
1740
1741//     let fs = FakeFs::new(cx.background());
1742//     fs.insert_tree("/dir", json!({ "a.rs": "one two three" }))
1743//         .await;
1744
1745//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1746
1747//     project.update(cx, |project, cx| {
1748//         project
1749//             .update_diagnostic_entries(
1750//                 LanguageServerId(0),
1751//                 Path::new("/dir/a.rs").to_owned(),
1752//                 None,
1753//                 vec![DiagnosticEntry {
1754//                     range: Unclipped(PointUtf16::new(0, 0))..Unclipped(PointUtf16::new(0, 3)),
1755//                     diagnostic: Diagnostic {
1756//                         severity: DiagnosticSeverity::ERROR,
1757//                         is_primary: true,
1758//                         message: "syntax error a1".to_string(),
1759//                         ..Default::default()
1760//                     },
1761//                 }],
1762//                 cx,
1763//             )
1764//             .unwrap();
1765//         project
1766//             .update_diagnostic_entries(
1767//                 LanguageServerId(1),
1768//                 Path::new("/dir/a.rs").to_owned(),
1769//                 None,
1770//                 vec![DiagnosticEntry {
1771//                     range: Unclipped(PointUtf16::new(0, 0))..Unclipped(PointUtf16::new(0, 3)),
1772//                     diagnostic: Diagnostic {
1773//                         severity: DiagnosticSeverity::ERROR,
1774//                         is_primary: true,
1775//                         message: "syntax error b1".to_string(),
1776//                         ..Default::default()
1777//                     },
1778//                 }],
1779//                 cx,
1780//             )
1781//             .unwrap();
1782
1783//         assert_eq!(
1784//             project.diagnostic_summary(cx),
1785//             DiagnosticSummary {
1786//                 error_count: 2,
1787//                 warning_count: 0,
1788//             }
1789//         );
1790//     });
1791// }
1792
1793// #[gpui::test]
1794// async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
1795//     init_test(cx);
1796
1797//     let mut language = Language::new(
1798//         LanguageConfig {
1799//             name: "Rust".into(),
1800//             path_suffixes: vec!["rs".to_string()],
1801//             ..Default::default()
1802//         },
1803//         Some(tree_sitter_rust::language()),
1804//     );
1805//     let mut fake_servers = language.set_fake_lsp_adapter(Default::default()).await;
1806
1807//     let text = "
1808//         fn a() {
1809//             f1();
1810//         }
1811//         fn b() {
1812//             f2();
1813//         }
1814//         fn c() {
1815//             f3();
1816//         }
1817//     "
1818//     .unindent();
1819
1820//     let fs = FakeFs::new(cx.background());
1821//     fs.insert_tree(
1822//         "/dir",
1823//         json!({
1824//             "a.rs": text.clone(),
1825//         }),
1826//     )
1827//     .await;
1828
1829//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1830//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
1831//     let buffer = project
1832//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1833//         .await
1834//         .unwrap();
1835
1836//     let mut fake_server = fake_servers.next().await.unwrap();
1837//     let lsp_document_version = fake_server
1838//         .receive_notification::<lsp2::notification::DidOpenTextDocument>()
1839//         .await
1840//         .text_document
1841//         .version;
1842
1843//     // Simulate editing the buffer after the language server computes some edits.
1844//     buffer.update(cx, |buffer, cx| {
1845//         buffer.edit(
1846//             [(
1847//                 Point::new(0, 0)..Point::new(0, 0),
1848//                 "// above first function\n",
1849//             )],
1850//             None,
1851//             cx,
1852//         );
1853//         buffer.edit(
1854//             [(
1855//                 Point::new(2, 0)..Point::new(2, 0),
1856//                 "    // inside first function\n",
1857//             )],
1858//             None,
1859//             cx,
1860//         );
1861//         buffer.edit(
1862//             [(
1863//                 Point::new(6, 4)..Point::new(6, 4),
1864//                 "// inside second function ",
1865//             )],
1866//             None,
1867//             cx,
1868//         );
1869
1870//         assert_eq!(
1871//             buffer.text(),
1872//             "
1873//                 // above first function
1874//                 fn a() {
1875//                     // inside first function
1876//                     f1();
1877//                 }
1878//                 fn b() {
1879//                     // inside second function f2();
1880//                 }
1881//                 fn c() {
1882//                     f3();
1883//                 }
1884//             "
1885//             .unindent()
1886//         );
1887//     });
1888
1889//     let edits = project
1890//         .update(cx, |project, cx| {
1891//             project.edits_from_lsp(
1892//                 &buffer,
1893//                 vec![
1894//                     // replace body of first function
1895//                     lsp2::TextEdit {
1896//                         range: lsp2::Range::new(
1897//                             lsp2::Position::new(0, 0),
1898//                             lsp2::Position::new(3, 0),
1899//                         ),
1900//                         new_text: "
1901//                             fn a() {
1902//                                 f10();
1903//                             }
1904//                             "
1905//                         .unindent(),
1906//                     },
1907//                     // edit inside second function
1908//                     lsp2::TextEdit {
1909//                         range: lsp2::Range::new(
1910//                             lsp2::Position::new(4, 6),
1911//                             lsp2::Position::new(4, 6),
1912//                         ),
1913//                         new_text: "00".into(),
1914//                     },
1915//                     // edit inside third function via two distinct edits
1916//                     lsp2::TextEdit {
1917//                         range: lsp2::Range::new(
1918//                             lsp2::Position::new(7, 5),
1919//                             lsp2::Position::new(7, 5),
1920//                         ),
1921//                         new_text: "4000".into(),
1922//                     },
1923//                     lsp2::TextEdit {
1924//                         range: lsp2::Range::new(
1925//                             lsp2::Position::new(7, 5),
1926//                             lsp2::Position::new(7, 6),
1927//                         ),
1928//                         new_text: "".into(),
1929//                     },
1930//                 ],
1931//                 LanguageServerId(0),
1932//                 Some(lsp_document_version),
1933//                 cx,
1934//             )
1935//         })
1936//         .await
1937//         .unwrap();
1938
1939//     buffer.update(cx, |buffer, cx| {
1940//         for (range, new_text) in edits {
1941//             buffer.edit([(range, new_text)], None, cx);
1942//         }
1943//         assert_eq!(
1944//             buffer.text(),
1945//             "
1946//                 // above first function
1947//                 fn a() {
1948//                     // inside first function
1949//                     f10();
1950//                 }
1951//                 fn b() {
1952//                     // inside second function f200();
1953//                 }
1954//                 fn c() {
1955//                     f4000();
1956//                 }
1957//                 "
1958//             .unindent()
1959//         );
1960//     });
1961// }
1962
1963// #[gpui::test]
1964// async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui::TestAppContext) {
1965//     init_test(cx);
1966
1967//     let text = "
1968//         use a::b;
1969//         use a::c;
1970
1971//         fn f() {
1972//             b();
1973//             c();
1974//         }
1975//     "
1976//     .unindent();
1977
1978//     let fs = FakeFs::new(cx.background());
1979//     fs.insert_tree(
1980//         "/dir",
1981//         json!({
1982//             "a.rs": text.clone(),
1983//         }),
1984//     )
1985//     .await;
1986
1987//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
1988//     let buffer = project
1989//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
1990//         .await
1991//         .unwrap();
1992
1993//     // Simulate the language server sending us a small edit in the form of a very large diff.
1994//     // Rust-analyzer does this when performing a merge-imports code action.
1995//     let edits = project
1996//         .update(cx, |project, cx| {
1997//             project.edits_from_lsp(
1998//                 &buffer,
1999//                 [
2000//                     // Replace the first use statement without editing the semicolon.
2001//                     lsp2::TextEdit {
2002//                         range: lsp2::Range::new(
2003//                             lsp2::Position::new(0, 4),
2004//                             lsp2::Position::new(0, 8),
2005//                         ),
2006//                         new_text: "a::{b, c}".into(),
2007//                     },
2008//                     // Reinsert the remainder of the file between the semicolon and the final
2009//                     // newline of the file.
2010//                     lsp2::TextEdit {
2011//                         range: lsp2::Range::new(
2012//                             lsp2::Position::new(0, 9),
2013//                             lsp2::Position::new(0, 9),
2014//                         ),
2015//                         new_text: "\n\n".into(),
2016//                     },
2017//                     lsp2::TextEdit {
2018//                         range: lsp2::Range::new(
2019//                             lsp2::Position::new(0, 9),
2020//                             lsp2::Position::new(0, 9),
2021//                         ),
2022//                         new_text: "
2023//                             fn f() {
2024//                                 b();
2025//                                 c();
2026//                             }"
2027//                         .unindent(),
2028//                     },
2029//                     // Delete everything after the first newline of the file.
2030//                     lsp2::TextEdit {
2031//                         range: lsp2::Range::new(
2032//                             lsp2::Position::new(1, 0),
2033//                             lsp2::Position::new(7, 0),
2034//                         ),
2035//                         new_text: "".into(),
2036//                     },
2037//                 ],
2038//                 LanguageServerId(0),
2039//                 None,
2040//                 cx,
2041//             )
2042//         })
2043//         .await
2044//         .unwrap();
2045
2046//     buffer.update(cx, |buffer, cx| {
2047//         let edits = edits
2048//             .into_iter()
2049//             .map(|(range, text)| {
2050//                 (
2051//                     range.start.to_point(buffer)..range.end.to_point(buffer),
2052//                     text,
2053//                 )
2054//             })
2055//             .collect::<Vec<_>>();
2056
2057//         assert_eq!(
2058//             edits,
2059//             [
2060//                 (Point::new(0, 4)..Point::new(0, 8), "a::{b, c}".into()),
2061//                 (Point::new(1, 0)..Point::new(2, 0), "".into())
2062//             ]
2063//         );
2064
2065//         for (range, new_text) in edits {
2066//             buffer.edit([(range, new_text)], None, cx);
2067//         }
2068//         assert_eq!(
2069//             buffer.text(),
2070//             "
2071//                 use a::{b, c};
2072
2073//                 fn f() {
2074//                     b();
2075//                     c();
2076//                 }
2077//             "
2078//             .unindent()
2079//         );
2080//     });
2081// }
2082
2083// #[gpui::test]
2084// async fn test_invalid_edits_from_lsp2(cx: &mut gpui::TestAppContext) {
2085//     init_test(cx);
2086
2087//     let text = "
2088//         use a::b;
2089//         use a::c;
2090
2091//         fn f() {
2092//             b();
2093//             c();
2094//         }
2095//     "
2096//     .unindent();
2097
2098//     let fs = FakeFs::new(cx.background());
2099//     fs.insert_tree(
2100//         "/dir",
2101//         json!({
2102//             "a.rs": text.clone(),
2103//         }),
2104//     )
2105//     .await;
2106
2107//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
2108//     let buffer = project
2109//         .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
2110//         .await
2111//         .unwrap();
2112
2113//     // Simulate the language server sending us edits in a non-ordered fashion,
2114//     // with ranges sometimes being inverted or pointing to invalid locations.
2115//     let edits = project
2116//         .update(cx, |project, cx| {
2117//             project.edits_from_lsp(
2118//                 &buffer,
2119//                 [
2120//                     lsp2::TextEdit {
2121//                         range: lsp2::Range::new(
2122//                             lsp2::Position::new(0, 9),
2123//                             lsp2::Position::new(0, 9),
2124//                         ),
2125//                         new_text: "\n\n".into(),
2126//                     },
2127//                     lsp2::TextEdit {
2128//                         range: lsp2::Range::new(
2129//                             lsp2::Position::new(0, 8),
2130//                             lsp2::Position::new(0, 4),
2131//                         ),
2132//                         new_text: "a::{b, c}".into(),
2133//                     },
2134//                     lsp2::TextEdit {
2135//                         range: lsp2::Range::new(
2136//                             lsp2::Position::new(1, 0),
2137//                             lsp2::Position::new(99, 0),
2138//                         ),
2139//                         new_text: "".into(),
2140//                     },
2141//                     lsp2::TextEdit {
2142//                         range: lsp2::Range::new(
2143//                             lsp2::Position::new(0, 9),
2144//                             lsp2::Position::new(0, 9),
2145//                         ),
2146//                         new_text: "
2147//                             fn f() {
2148//                                 b();
2149//                                 c();
2150//                             }"
2151//                         .unindent(),
2152//                     },
2153//                 ],
2154//                 LanguageServerId(0),
2155//                 None,
2156//                 cx,
2157//             )
2158//         })
2159//         .await
2160//         .unwrap();
2161
2162//     buffer.update(cx, |buffer, cx| {
2163//         let edits = edits
2164//             .into_iter()
2165//             .map(|(range, text)| {
2166//                 (
2167//                     range.start.to_point(buffer)..range.end.to_point(buffer),
2168//                     text,
2169//                 )
2170//             })
2171//             .collect::<Vec<_>>();
2172
2173//         assert_eq!(
2174//             edits,
2175//             [
2176//                 (Point::new(0, 4)..Point::new(0, 8), "a::{b, c}".into()),
2177//                 (Point::new(1, 0)..Point::new(2, 0), "".into())
2178//             ]
2179//         );
2180
2181//         for (range, new_text) in edits {
2182//             buffer.edit([(range, new_text)], None, cx);
2183//         }
2184//         assert_eq!(
2185//             buffer.text(),
2186//             "
2187//                 use a::{b, c};
2188
2189//                 fn f() {
2190//                     b();
2191//                     c();
2192//                 }
2193//             "
2194//             .unindent()
2195//         );
2196//     });
2197// }
2198
2199// fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
2200//     buffer: &Buffer,
2201//     range: Range<T>,
2202// ) -> Vec<(String, Option<DiagnosticSeverity>)> {
2203//     let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
2204//     for chunk in buffer.snapshot().chunks(range, true) {
2205//         if chunks.last().map_or(false, |prev_chunk| {
2206//             prev_chunk.1 == chunk.diagnostic_severity
2207//         }) {
2208//             chunks.last_mut().unwrap().0.push_str(chunk.text);
2209//         } else {
2210//             chunks.push((chunk.text.to_string(), chunk.diagnostic_severity));
2211//         }
2212//     }
2213//     chunks
2214// }
2215
2216// #[gpui::test(iterations = 10)]
2217// async fn test_definition(cx: &mut gpui::TestAppContext) {
2218//     init_test(cx);
2219
2220//     let mut language = Language::new(
2221//         LanguageConfig {
2222//             name: "Rust".into(),
2223//             path_suffixes: vec!["rs".to_string()],
2224//             ..Default::default()
2225//         },
2226//         Some(tree_sitter_rust::language()),
2227//     );
2228//     let mut fake_servers = language.set_fake_lsp_adapter(Default::default()).await;
2229
2230//     let fs = FakeFs::new(cx.background());
2231//     fs.insert_tree(
2232//         "/dir",
2233//         json!({
2234//             "a.rs": "const fn a() { A }",
2235//             "b.rs": "const y: i32 = crate::a()",
2236//         }),
2237//     )
2238//     .await;
2239
2240//     let project = Project::test(fs, ["/dir/b.rs".as_ref()], cx).await;
2241//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
2242
2243//     let buffer = project
2244//         .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
2245//         .await
2246//         .unwrap();
2247
2248//     let fake_server = fake_servers.next().await.unwrap();
2249//     fake_server.handle_request::<lsp2::request::GotoDefinition, _, _>(|params, _| async move {
2250//         let params = params.text_document_position_params;
2251//         assert_eq!(
2252//             params.text_document.uri.to_file_path().unwrap(),
2253//             Path::new("/dir/b.rs"),
2254//         );
2255//         assert_eq!(params.position, lsp2::Position::new(0, 22));
2256
2257//         Ok(Some(lsp2::GotoDefinitionResponse::Scalar(
2258//             lsp2::Location::new(
2259//                 lsp2::Url::from_file_path("/dir/a.rs").unwrap(),
2260//                 lsp2::Range::new(lsp2::Position::new(0, 9), lsp2::Position::new(0, 10)),
2261//             ),
2262//         )))
2263//     });
2264
2265//     let mut definitions = project
2266//         .update(cx, |project, cx| project.definition(&buffer, 22, cx))
2267//         .await
2268//         .unwrap();
2269
2270//     // Assert no new language server started
2271//     cx.foreground().run_until_parked();
2272//     assert!(fake_servers.try_next().is_err());
2273
2274//     assert_eq!(definitions.len(), 1);
2275//     let definition = definitions.pop().unwrap();
2276//     cx.update(|cx| {
2277//         let target_buffer = definition.target.buffer.read(cx);
2278//         assert_eq!(
2279//             target_buffer
2280//                 .file()
2281//                 .unwrap()
2282//                 .as_local()
2283//                 .unwrap()
2284//                 .abs_path(cx),
2285//             Path::new("/dir/a.rs"),
2286//         );
2287//         assert_eq!(definition.target.range.to_offset(target_buffer), 9..10);
2288//         assert_eq!(
2289//             list_worktrees(&project, cx),
2290//             [("/dir/b.rs".as_ref(), true), ("/dir/a.rs".as_ref(), false)]
2291//         );
2292
2293//         drop(definition);
2294//     });
2295//     cx.read(|cx| {
2296//         assert_eq!(list_worktrees(&project, cx), [("/dir/b.rs".as_ref(), true)]);
2297//     });
2298
2299//     fn list_worktrees<'a>(
2300//         project: &'a ModelHandle<Project>,
2301//         cx: &'a AppContext,
2302//     ) -> Vec<(&'a Path, bool)> {
2303//         project
2304//             .read(cx)
2305//             .worktrees(cx)
2306//             .map(|worktree| {
2307//                 let worktree = worktree.read(cx);
2308//                 (
2309//                     worktree.as_local().unwrap().abs_path().as_ref(),
2310//                     worktree.is_visible(),
2311//                 )
2312//             })
2313//             .collect::<Vec<_>>()
2314//     }
2315// }
2316
2317// #[gpui::test]
2318// async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
2319//     init_test(cx);
2320
2321//     let mut language = Language::new(
2322//         LanguageConfig {
2323//             name: "TypeScript".into(),
2324//             path_suffixes: vec!["ts".to_string()],
2325//             ..Default::default()
2326//         },
2327//         Some(tree_sitter_typescript::language_typescript()),
2328//     );
2329//     let mut fake_language_servers = language
2330//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2331//             capabilities: lsp::ServerCapabilities {
2332//                 completion_provider: Some(lsp::CompletionOptions {
2333//                     trigger_characters: Some(vec![":".to_string()]),
2334//                     ..Default::default()
2335//                 }),
2336//                 ..Default::default()
2337//             },
2338//             ..Default::default()
2339//         }))
2340//         .await;
2341
2342//     let fs = FakeFs::new(cx.background());
2343//     fs.insert_tree(
2344//         "/dir",
2345//         json!({
2346//             "a.ts": "",
2347//         }),
2348//     )
2349//     .await;
2350
2351//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
2352//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
2353//     let buffer = project
2354//         .update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
2355//         .await
2356//         .unwrap();
2357
2358//     let fake_server = fake_language_servers.next().await.unwrap();
2359
2360//     let text = "let a = b.fqn";
2361//     buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
2362//     let completions = project.update(cx, |project, cx| {
2363//         project.completions(&buffer, text.len(), cx)
2364//     });
2365
2366//     fake_server
2367//         .handle_request::<lsp2::request::Completion, _, _>(|_, _| async move {
2368//             Ok(Some(lsp2::CompletionResponse::Array(vec![
2369//                 lsp2::CompletionItem {
2370//                     label: "fullyQualifiedName?".into(),
2371//                     insert_text: Some("fullyQualifiedName".into()),
2372//                     ..Default::default()
2373//                 },
2374//             ])))
2375//         })
2376//         .next()
2377//         .await;
2378//     let completions = completions.await.unwrap();
2379//     let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
2380//     assert_eq!(completions.len(), 1);
2381//     assert_eq!(completions[0].new_text, "fullyQualifiedName");
2382//     assert_eq!(
2383//         completions[0].old_range.to_offset(&snapshot),
2384//         text.len() - 3..text.len()
2385//     );
2386
2387//     let text = "let a = \"atoms/cmp\"";
2388//     buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
2389//     let completions = project.update(cx, |project, cx| {
2390//         project.completions(&buffer, text.len() - 1, cx)
2391//     });
2392
2393//     fake_server
2394//         .handle_request::<lsp2::request::Completion, _, _>(|_, _| async move {
2395//             Ok(Some(lsp2::CompletionResponse::Array(vec![
2396//                 lsp2::CompletionItem {
2397//                     label: "component".into(),
2398//                     ..Default::default()
2399//                 },
2400//             ])))
2401//         })
2402//         .next()
2403//         .await;
2404//     let completions = completions.await.unwrap();
2405//     let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
2406//     assert_eq!(completions.len(), 1);
2407//     assert_eq!(completions[0].new_text, "component");
2408//     assert_eq!(
2409//         completions[0].old_range.to_offset(&snapshot),
2410//         text.len() - 4..text.len() - 1
2411//     );
2412// }
2413
2414// #[gpui::test]
2415// async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
2416//     init_test(cx);
2417
2418//     let mut language = Language::new(
2419//         LanguageConfig {
2420//             name: "TypeScript".into(),
2421//             path_suffixes: vec!["ts".to_string()],
2422//             ..Default::default()
2423//         },
2424//         Some(tree_sitter_typescript::language_typescript()),
2425//     );
2426//     let mut fake_language_servers = language
2427//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2428//             capabilities: lsp::ServerCapabilities {
2429//                 completion_provider: Some(lsp::CompletionOptions {
2430//                     trigger_characters: Some(vec![":".to_string()]),
2431//                     ..Default::default()
2432//                 }),
2433//                 ..Default::default()
2434//             },
2435//             ..Default::default()
2436//         }))
2437//         .await;
2438
2439//     let fs = FakeFs::new(cx.background());
2440//     fs.insert_tree(
2441//         "/dir",
2442//         json!({
2443//             "a.ts": "",
2444//         }),
2445//     )
2446//     .await;
2447
2448//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
2449//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
2450//     let buffer = project
2451//         .update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
2452//         .await
2453//         .unwrap();
2454
2455//     let fake_server = fake_language_servers.next().await.unwrap();
2456
2457//     let text = "let a = b.fqn";
2458//     buffer.update(cx, |buffer, cx| buffer.set_text(text, cx));
2459//     let completions = project.update(cx, |project, cx| {
2460//         project.completions(&buffer, text.len(), cx)
2461//     });
2462
2463//     fake_server
2464//         .handle_request::<lsp2::request::Completion, _, _>(|_, _| async move {
2465//             Ok(Some(lsp2::CompletionResponse::Array(vec![
2466//                 lsp2::CompletionItem {
2467//                     label: "fullyQualifiedName?".into(),
2468//                     insert_text: Some("fully\rQualified\r\nName".into()),
2469//                     ..Default::default()
2470//                 },
2471//             ])))
2472//         })
2473//         .next()
2474//         .await;
2475//     let completions = completions.await.unwrap();
2476//     assert_eq!(completions.len(), 1);
2477//     assert_eq!(completions[0].new_text, "fully\nQualified\nName");
2478// }
2479
2480// #[gpui::test(iterations = 10)]
2481// async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
2482//     init_test(cx);
2483
2484//     let mut language = Language::new(
2485//         LanguageConfig {
2486//             name: "TypeScript".into(),
2487//             path_suffixes: vec!["ts".to_string()],
2488//             ..Default::default()
2489//         },
2490//         None,
2491//     );
2492//     let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
2493
2494//     let fs = FakeFs::new(cx.background());
2495//     fs.insert_tree(
2496//         "/dir",
2497//         json!({
2498//             "a.ts": "a",
2499//         }),
2500//     )
2501//     .await;
2502
2503//     let project = Project::test(fs, ["/dir".as_ref()], cx).await;
2504//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
2505//     let buffer = project
2506//         .update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
2507//         .await
2508//         .unwrap();
2509
2510//     let fake_server = fake_language_servers.next().await.unwrap();
2511
2512//     // Language server returns code actions that contain commands, and not edits.
2513//     let actions = project.update(cx, |project, cx| project.code_actions(&buffer, 0..0, cx));
2514//     fake_server
2515//         .handle_request::<lsp2::request::CodeActionRequest, _, _>(|_, _| async move {
2516//             Ok(Some(vec![
2517//                 lsp2::CodeActionOrCommand::CodeAction(lsp2::CodeAction {
2518//                     title: "The code action".into(),
2519//                     command: Some(lsp::Command {
2520//                         title: "The command".into(),
2521//                         command: "_the/command".into(),
2522//                         arguments: Some(vec![json!("the-argument")]),
2523//                     }),
2524//                     ..Default::default()
2525//                 }),
2526//                 lsp2::CodeActionOrCommand::CodeAction(lsp2::CodeAction {
2527//                     title: "two".into(),
2528//                     ..Default::default()
2529//                 }),
2530//             ]))
2531//         })
2532//         .next()
2533//         .await;
2534
2535//     let action = actions.await.unwrap()[0].clone();
2536//     let apply = project.update(cx, |project, cx| {
2537//         project.apply_code_action(buffer.clone(), action, true, cx)
2538//     });
2539
2540//     // Resolving the code action does not populate its edits. In absence of
2541//     // edits, we must execute the given command.
2542//     fake_server.handle_request::<lsp2::request::CodeActionResolveRequest, _, _>(
2543//         |action, _| async move { Ok(action) },
2544//     );
2545
2546//     // While executing the command, the language server sends the editor
2547//     // a `workspaceEdit` request.
2548//     fake_server
2549//         .handle_request::<lsp2::request::ExecuteCommand, _, _>({
2550//             let fake = fake_server.clone();
2551//             move |params, _| {
2552//                 assert_eq!(params.command, "_the/command");
2553//                 let fake = fake.clone();
2554//                 async move {
2555//                     fake.server
2556//                         .request::<lsp2::request::ApplyWorkspaceEdit>(
2557//                             lsp2::ApplyWorkspaceEditParams {
2558//                                 label: None,
2559//                                 edit: lsp::WorkspaceEdit {
2560//                                     changes: Some(
2561//                                         [(
2562//                                             lsp2::Url::from_file_path("/dir/a.ts").unwrap(),
2563//                                             vec![lsp2::TextEdit {
2564//                                                 range: lsp2::Range::new(
2565//                                                     lsp2::Position::new(0, 0),
2566//                                                     lsp2::Position::new(0, 0),
2567//                                                 ),
2568//                                                 new_text: "X".into(),
2569//                                             }],
2570//                                         )]
2571//                                         .into_iter()
2572//                                         .collect(),
2573//                                     ),
2574//                                     ..Default::default()
2575//                                 },
2576//                             },
2577//                         )
2578//                         .await
2579//                         .unwrap();
2580//                     Ok(Some(json!(null)))
2581//                 }
2582//             }
2583//         })
2584//         .next()
2585//         .await;
2586
2587//     // Applying the code action returns a project transaction containing the edits
2588//     // sent by the language server in its `workspaceEdit` request.
2589//     let transaction = apply.await.unwrap();
2590//     assert!(transaction.0.contains_key(&buffer));
2591//     buffer.update(cx, |buffer, cx| {
2592//         assert_eq!(buffer.text(), "Xa");
2593//         buffer.undo(cx);
2594//         assert_eq!(buffer.text(), "a");
2595//     });
2596// }
2597
2598// #[gpui::test(iterations = 10)]
2599// async fn test_save_file(cx: &mut gpui::TestAppContext) {
2600//     init_test(cx);
2601
2602//     let fs = FakeFs::new(cx.background());
2603//     fs.insert_tree(
2604//         "/dir",
2605//         json!({
2606//             "file1": "the old contents",
2607//         }),
2608//     )
2609//     .await;
2610
2611//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
2612//     let buffer = project
2613//         .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
2614//         .await
2615//         .unwrap();
2616//     buffer.update(cx, |buffer, cx| {
2617//         assert_eq!(buffer.text(), "the old contents");
2618//         buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], None, cx);
2619//     });
2620
2621//     project
2622//         .update(cx, |project, cx| project.save_buffer(buffer.clone(), cx))
2623//         .await
2624//         .unwrap();
2625
2626//     let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
2627//     assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text()));
2628// }
2629
2630// #[gpui::test]
2631// async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
2632//     init_test(cx);
2633
2634//     let fs = FakeFs::new(cx.background());
2635//     fs.insert_tree(
2636//         "/dir",
2637//         json!({
2638//             "file1": "the old contents",
2639//         }),
2640//     )
2641//     .await;
2642
2643//     let project = Project::test(fs.clone(), ["/dir/file1".as_ref()], cx).await;
2644//     let buffer = project
2645//         .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
2646//         .await
2647//         .unwrap();
2648//     buffer.update(cx, |buffer, cx| {
2649//         buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], None, cx);
2650//     });
2651
2652//     project
2653//         .update(cx, |project, cx| project.save_buffer(buffer.clone(), cx))
2654//         .await
2655//         .unwrap();
2656
2657//     let new_text = fs.load(Path::new("/dir/file1")).await.unwrap();
2658//     assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text()));
2659// }
2660
2661// #[gpui::test]
2662// async fn test_save_as(cx: &mut gpui::TestAppContext) {
2663//     init_test(cx);
2664
2665//     let fs = FakeFs::new(cx.background());
2666//     fs.insert_tree("/dir", json!({})).await;
2667
2668//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
2669
2670//     let languages = project.read_with(cx, |project, _| project.languages().clone());
2671//     languages.register(
2672//         "/some/path",
2673//         LanguageConfig {
2674//             name: "Rust".into(),
2675//             path_suffixes: vec!["rs".into()],
2676//             ..Default::default()
2677//         },
2678//         tree_sitter_rust::language(),
2679//         vec![],
2680//         |_| Default::default(),
2681//     );
2682
2683//     let buffer = project.update(cx, |project, cx| {
2684//         project.create_buffer("", None, cx).unwrap()
2685//     });
2686//     buffer.update(cx, |buffer, cx| {
2687//         buffer.edit([(0..0, "abc")], None, cx);
2688//         assert!(buffer.is_dirty());
2689//         assert!(!buffer.has_conflict());
2690//         assert_eq!(buffer.language().unwrap().name().as_ref(), "Plain Text");
2691//     });
2692//     project
2693//         .update(cx, |project, cx| {
2694//             project.save_buffer_as(buffer.clone(), "/dir/file1.rs".into(), cx)
2695//         })
2696//         .await
2697//         .unwrap();
2698//     assert_eq!(fs.load(Path::new("/dir/file1.rs")).await.unwrap(), "abc");
2699
2700//     cx.foreground().run_until_parked();
2701//     buffer.read_with(cx, |buffer, cx| {
2702//         assert_eq!(
2703//             buffer.file().unwrap().full_path(cx),
2704//             Path::new("dir/file1.rs")
2705//         );
2706//         assert!(!buffer.is_dirty());
2707//         assert!(!buffer.has_conflict());
2708//         assert_eq!(buffer.language().unwrap().name().as_ref(), "Rust");
2709//     });
2710
2711//     let opened_buffer = project
2712//         .update(cx, |project, cx| {
2713//             project.open_local_buffer("/dir/file1.rs", cx)
2714//         })
2715//         .await
2716//         .unwrap();
2717//     assert_eq!(opened_buffer, buffer);
2718// }
2719
2720// #[gpui::test(retries = 5)]
2721// async fn test_rescan_and_remote_updates(
2722//     deterministic: Arc<Deterministic>,
2723//     cx: &mut gpui::TestAppContext,
2724// ) {
2725//     init_test(cx);
2726//     cx.foreground().allow_parking();
2727
2728//     let dir = temp_tree(json!({
2729//         "a": {
2730//             "file1": "",
2731//             "file2": "",
2732//             "file3": "",
2733//         },
2734//         "b": {
2735//             "c": {
2736//                 "file4": "",
2737//                 "file5": "",
2738//             }
2739//         }
2740//     }));
2741
2742//     let project = Project::test(Arc::new(RealFs), [dir.path()], cx).await;
2743//     let rpc = project.read_with(cx, |p, _| p.client.clone());
2744
2745//     let buffer_for_path = |path: &'static str, cx: &mut gpui2::TestAppContext| {
2746//         let buffer = project.update(cx, |p, cx| p.open_local_buffer(dir.path().join(path), cx));
2747//         async move { buffer.await.unwrap() }
2748//     };
2749//     let id_for_path = |path: &'static str, cx: &gpui2::TestAppContext| {
2750//         project.read_with(cx, |project, cx| {
2751//             let tree = project.worktrees(cx).next().unwrap();
2752//             tree.read(cx)
2753//                 .entry_for_path(path)
2754//                 .unwrap_or_else(|| panic!("no entry for path {}", path))
2755//                 .id
2756//         })
2757//     };
2758
2759//     let buffer2 = buffer_for_path("a/file2", cx).await;
2760//     let buffer3 = buffer_for_path("a/file3", cx).await;
2761//     let buffer4 = buffer_for_path("b/c/file4", cx).await;
2762//     let buffer5 = buffer_for_path("b/c/file5", cx).await;
2763
2764//     let file2_id = id_for_path("a/file2", cx);
2765//     let file3_id = id_for_path("a/file3", cx);
2766//     let file4_id = id_for_path("b/c/file4", cx);
2767
2768//     // Create a remote copy of this worktree.
2769//     let tree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap());
2770
2771//     let metadata = tree.read_with(cx, |tree, _| tree.as_local().unwrap().metadata_proto());
2772
2773//     let updates = Arc::new(Mutex::new(Vec::new()));
2774//     tree.update(cx, |tree, cx| {
2775//         let _ = tree.as_local_mut().unwrap().observe_updates(0, cx, {
2776//             let updates = updates.clone();
2777//             move |update| {
2778//                 updates.lock().push(update);
2779//                 async { true }
2780//             }
2781//         });
2782//     });
2783
2784//     let remote = cx.update(|cx| Worktree::remote(1, 1, metadata, rpc.clone(), cx));
2785//     deterministic.run_until_parked();
2786
2787//     cx.read(|cx| {
2788//         assert!(!buffer2.read(cx).is_dirty());
2789//         assert!(!buffer3.read(cx).is_dirty());
2790//         assert!(!buffer4.read(cx).is_dirty());
2791//         assert!(!buffer5.read(cx).is_dirty());
2792//     });
2793
2794//     // Rename and delete files and directories.
2795//     tree.flush_fs_events(cx).await;
2796//     std::fs::rename(dir.path().join("a/file3"), dir.path().join("b/c/file3")).unwrap();
2797//     std::fs::remove_file(dir.path().join("b/c/file5")).unwrap();
2798//     std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
2799//     std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap();
2800//     tree.flush_fs_events(cx).await;
2801
2802//     let expected_paths = vec![
2803//         "a",
2804//         "a/file1",
2805//         "a/file2.new",
2806//         "b",
2807//         "d",
2808//         "d/file3",
2809//         "d/file4",
2810//     ];
2811
2812//     cx.read(|app| {
2813//         assert_eq!(
2814//             tree.read(app)
2815//                 .paths()
2816//                 .map(|p| p.to_str().unwrap())
2817//                 .collect::<Vec<_>>(),
2818//             expected_paths
2819//         );
2820
2821//         assert_eq!(id_for_path("a/file2.new", cx), file2_id);
2822//         assert_eq!(id_for_path("d/file3", cx), file3_id);
2823//         assert_eq!(id_for_path("d/file4", cx), file4_id);
2824
2825//         assert_eq!(
2826//             buffer2.read(app).file().unwrap().path().as_ref(),
2827//             Path::new("a/file2.new")
2828//         );
2829//         assert_eq!(
2830//             buffer3.read(app).file().unwrap().path().as_ref(),
2831//             Path::new("d/file3")
2832//         );
2833//         assert_eq!(
2834//             buffer4.read(app).file().unwrap().path().as_ref(),
2835//             Path::new("d/file4")
2836//         );
2837//         assert_eq!(
2838//             buffer5.read(app).file().unwrap().path().as_ref(),
2839//             Path::new("b/c/file5")
2840//         );
2841
2842//         assert!(!buffer2.read(app).file().unwrap().is_deleted());
2843//         assert!(!buffer3.read(app).file().unwrap().is_deleted());
2844//         assert!(!buffer4.read(app).file().unwrap().is_deleted());
2845//         assert!(buffer5.read(app).file().unwrap().is_deleted());
2846//     });
2847
2848//     // Update the remote worktree. Check that it becomes consistent with the
2849//     // local worktree.
2850//     deterministic.run_until_parked();
2851//     remote.update(cx, |remote, _| {
2852//         for update in updates.lock().drain(..) {
2853//             remote.as_remote_mut().unwrap().update_from_remote(update);
2854//         }
2855//     });
2856//     deterministic.run_until_parked();
2857//     remote.read_with(cx, |remote, _| {
2858//         assert_eq!(
2859//             remote
2860//                 .paths()
2861//                 .map(|p| p.to_str().unwrap())
2862//                 .collect::<Vec<_>>(),
2863//             expected_paths
2864//         );
2865//     });
2866// }
2867
2868// #[gpui::test(iterations = 10)]
2869// async fn test_buffer_identity_across_renames(
2870//     deterministic: Arc<Deterministic>,
2871//     cx: &mut gpui::TestAppContext,
2872// ) {
2873//     init_test(cx);
2874
2875//     let fs = FakeFs::new(cx.background());
2876//     fs.insert_tree(
2877//         "/dir",
2878//         json!({
2879//             "a": {
2880//                 "file1": "",
2881//             }
2882//         }),
2883//     )
2884//     .await;
2885
2886//     let project = Project::test(fs, [Path::new("/dir")], cx).await;
2887//     let tree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap());
2888//     let tree_id = tree.read_with(cx, |tree, _| tree.id());
2889
2890//     let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| {
2891//         project.read_with(cx, |project, cx| {
2892//             let tree = project.worktrees(cx).next().unwrap();
2893//             tree.read(cx)
2894//                 .entry_for_path(path)
2895//                 .unwrap_or_else(|| panic!("no entry for path {}", path))
2896//                 .id
2897//         })
2898//     };
2899
2900//     let dir_id = id_for_path("a", cx);
2901//     let file_id = id_for_path("a/file1", cx);
2902//     let buffer = project
2903//         .update(cx, |p, cx| p.open_buffer((tree_id, "a/file1"), cx))
2904//         .await
2905//         .unwrap();
2906//     buffer.read_with(cx, |buffer, _| assert!(!buffer.is_dirty()));
2907
2908//     project
2909//         .update(cx, |project, cx| {
2910//             project.rename_entry(dir_id, Path::new("b"), cx)
2911//         })
2912//         .unwrap()
2913//         .await
2914//         .unwrap();
2915//     deterministic.run_until_parked();
2916//     assert_eq!(id_for_path("b", cx), dir_id);
2917//     assert_eq!(id_for_path("b/file1", cx), file_id);
2918//     buffer.read_with(cx, |buffer, _| assert!(!buffer.is_dirty()));
2919// }
2920
2921// #[gpui2::test]
2922// async fn test_buffer_deduping(cx: &mut gpui2::TestAppContext) {
2923//     init_test(cx);
2924
2925//     let fs = FakeFs::new(cx.background());
2926//     fs.insert_tree(
2927//         "/dir",
2928//         json!({
2929//             "a.txt": "a-contents",
2930//             "b.txt": "b-contents",
2931//         }),
2932//     )
2933//     .await;
2934
2935//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
2936
2937//     // Spawn multiple tasks to open paths, repeating some paths.
2938//     let (buffer_a_1, buffer_b, buffer_a_2) = project.update(cx, |p, cx| {
2939//         (
2940//             p.open_local_buffer("/dir/a.txt", cx),
2941//             p.open_local_buffer("/dir/b.txt", cx),
2942//             p.open_local_buffer("/dir/a.txt", cx),
2943//         )
2944//     });
2945
2946//     let buffer_a_1 = buffer_a_1.await.unwrap();
2947//     let buffer_a_2 = buffer_a_2.await.unwrap();
2948//     let buffer_b = buffer_b.await.unwrap();
2949//     assert_eq!(buffer_a_1.read_with(cx, |b, _| b.text()), "a-contents");
2950//     assert_eq!(buffer_b.read_with(cx, |b, _| b.text()), "b-contents");
2951
2952//     // There is only one buffer per path.
2953//     let buffer_a_id = buffer_a_1.id();
2954//     assert_eq!(buffer_a_2.id(), buffer_a_id);
2955
2956//     // Open the same path again while it is still open.
2957//     drop(buffer_a_1);
2958//     let buffer_a_3 = project
2959//         .update(cx, |p, cx| p.open_local_buffer("/dir/a.txt", cx))
2960//         .await
2961//         .unwrap();
2962
2963//     // There's still only one buffer per path.
2964//     assert_eq!(buffer_a_3.id(), buffer_a_id);
2965// }
2966
2967// #[gpui2::test]
2968// async fn test_buffer_is_dirty(cx: &mut gpui2::TestAppContext) {
2969//     init_test(cx);
2970
2971//     let fs = FakeFs::new(cx.background());
2972//     fs.insert_tree(
2973//         "/dir",
2974//         json!({
2975//             "file1": "abc",
2976//             "file2": "def",
2977//             "file3": "ghi",
2978//         }),
2979//     )
2980//     .await;
2981
2982//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
2983
2984//     let buffer1 = project
2985//         .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
2986//         .await
2987//         .unwrap();
2988//     let events = Rc::new(RefCell::new(Vec::new()));
2989
2990//     // initially, the buffer isn't dirty.
2991//     buffer1.update(cx, |buffer, cx| {
2992//         cx.subscribe(&buffer1, {
2993//             let events = events.clone();
2994//             move |_, _, event, _| match event {
2995//                 BufferEvent::Operation(_) => {}
2996//                 _ => events.borrow_mut().push(event.clone()),
2997//             }
2998//         })
2999//         .detach();
3000
3001//         assert!(!buffer.is_dirty());
3002//         assert!(events.borrow().is_empty());
3003
3004//         buffer.edit([(1..2, "")], None, cx);
3005//     });
3006
3007//     // after the first edit, the buffer is dirty, and emits a dirtied event.
3008//     buffer1.update(cx, |buffer, cx| {
3009//         assert!(buffer.text() == "ac");
3010//         assert!(buffer.is_dirty());
3011//         assert_eq!(
3012//             *events.borrow(),
3013//             &[language2::Event::Edited, language2::Event::DirtyChanged]
3014//         );
3015//         events.borrow_mut().clear();
3016//         buffer.did_save(
3017//             buffer.version(),
3018//             buffer.as_rope().fingerprint(),
3019//             buffer.file().unwrap().mtime(),
3020//             cx,
3021//         );
3022//     });
3023
3024//     // after saving, the buffer is not dirty, and emits a saved event.
3025//     buffer1.update(cx, |buffer, cx| {
3026//         assert!(!buffer.is_dirty());
3027//         assert_eq!(*events.borrow(), &[language2::Event::Saved]);
3028//         events.borrow_mut().clear();
3029
3030//         buffer.edit([(1..1, "B")], None, cx);
3031//         buffer.edit([(2..2, "D")], None, cx);
3032//     });
3033
3034//     // after editing again, the buffer is dirty, and emits another dirty event.
3035//     buffer1.update(cx, |buffer, cx| {
3036//         assert!(buffer.text() == "aBDc");
3037//         assert!(buffer.is_dirty());
3038//         assert_eq!(
3039//             *events.borrow(),
3040//             &[
3041//                 language2::Event::Edited,
3042//                 language2::Event::DirtyChanged,
3043//                 language2::Event::Edited,
3044//             ],
3045//         );
3046//         events.borrow_mut().clear();
3047
3048//         // After restoring the buffer to its previously-saved state,
3049//         // the buffer is not considered dirty anymore.
3050//         buffer.edit([(1..3, "")], None, cx);
3051//         assert!(buffer.text() == "ac");
3052//         assert!(!buffer.is_dirty());
3053//     });
3054
3055//     assert_eq!(
3056//         *events.borrow(),
3057//         &[language2::Event::Edited, language2::Event::DirtyChanged]
3058//     );
3059
3060//     // When a file is deleted, the buffer is considered dirty.
3061//     let events = Rc::new(RefCell::new(Vec::new()));
3062//     let buffer2 = project
3063//         .update(cx, |p, cx| p.open_local_buffer("/dir/file2", cx))
3064//         .await
3065//         .unwrap();
3066//     buffer2.update(cx, |_, cx| {
3067//         cx.subscribe(&buffer2, {
3068//             let events = events.clone();
3069//             move |_, _, event, _| events.borrow_mut().push(event.clone())
3070//         })
3071//         .detach();
3072//     });
3073
3074//     fs.remove_file("/dir/file2".as_ref(), Default::default())
3075//         .await
3076//         .unwrap();
3077//     cx.foreground().run_until_parked();
3078//     buffer2.read_with(cx, |buffer, _| assert!(buffer.is_dirty()));
3079//     assert_eq!(
3080//         *events.borrow(),
3081//         &[
3082//             language2::Event::DirtyChanged,
3083//             language2::Event::FileHandleChanged
3084//         ]
3085//     );
3086
3087//     // When a file is already dirty when deleted, we don't emit a Dirtied event.
3088//     let events = Rc::new(RefCell::new(Vec::new()));
3089//     let buffer3 = project
3090//         .update(cx, |p, cx| p.open_local_buffer("/dir/file3", cx))
3091//         .await
3092//         .unwrap();
3093//     buffer3.update(cx, |_, cx| {
3094//         cx.subscribe(&buffer3, {
3095//             let events = events.clone();
3096//             move |_, _, event, _| events.borrow_mut().push(event.clone())
3097//         })
3098//         .detach();
3099//     });
3100
3101//     buffer3.update(cx, |buffer, cx| {
3102//         buffer.edit([(0..0, "x")], None, cx);
3103//     });
3104//     events.borrow_mut().clear();
3105//     fs.remove_file("/dir/file3".as_ref(), Default::default())
3106//         .await
3107//         .unwrap();
3108//     cx.foreground().run_until_parked();
3109//     assert_eq!(*events.borrow(), &[language2::Event::FileHandleChanged]);
3110//     cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
3111// }
3112
3113// #[gpui::test]
3114// async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
3115//     init_test(cx);
3116
3117//     let initial_contents = "aaa\nbbbbb\nc\n";
3118//     let fs = FakeFs::new(cx.background());
3119//     fs.insert_tree(
3120//         "/dir",
3121//         json!({
3122//             "the-file": initial_contents,
3123//         }),
3124//     )
3125//     .await;
3126//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3127//     let buffer = project
3128//         .update(cx, |p, cx| p.open_local_buffer("/dir/the-file", cx))
3129//         .await
3130//         .unwrap();
3131
3132//     let anchors = (0..3)
3133//         .map(|row| buffer.read_with(cx, |b, _| b.anchor_before(Point::new(row, 1))))
3134//         .collect::<Vec<_>>();
3135
3136//     // Change the file on disk, adding two new lines of text, and removing
3137//     // one line.
3138//     buffer.read_with(cx, |buffer, _| {
3139//         assert!(!buffer.is_dirty());
3140//         assert!(!buffer.has_conflict());
3141//     });
3142//     let new_contents = "AAAA\naaa\nBB\nbbbbb\n";
3143//     fs.save(
3144//         "/dir/the-file".as_ref(),
3145//         &new_contents.into(),
3146//         LineEnding::Unix,
3147//     )
3148//     .await
3149//     .unwrap();
3150
3151//     // Because the buffer was not modified, it is reloaded from disk. Its
3152//     // contents are edited according to the diff between the old and new
3153//     // file contents.
3154//     cx.foreground().run_until_parked();
3155//     buffer.update(cx, |buffer, _| {
3156//         assert_eq!(buffer.text(), new_contents);
3157//         assert!(!buffer.is_dirty());
3158//         assert!(!buffer.has_conflict());
3159
3160//         let anchor_positions = anchors
3161//             .iter()
3162//             .map(|anchor| anchor.to_point(&*buffer))
3163//             .collect::<Vec<_>>();
3164//         assert_eq!(
3165//             anchor_positions,
3166//             [Point::new(1, 1), Point::new(3, 1), Point::new(3, 5)]
3167//         );
3168//     });
3169
3170//     // Modify the buffer
3171//     buffer.update(cx, |buffer, cx| {
3172//         buffer.edit([(0..0, " ")], None, cx);
3173//         assert!(buffer.is_dirty());
3174//         assert!(!buffer.has_conflict());
3175//     });
3176
3177//     // Change the file on disk again, adding blank lines to the beginning.
3178//     fs.save(
3179//         "/dir/the-file".as_ref(),
3180//         &"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
3181//         LineEnding::Unix,
3182//     )
3183//     .await
3184//     .unwrap();
3185
3186//     // Because the buffer is modified, it doesn't reload from disk, but is
3187//     // marked as having a conflict.
3188//     cx.foreground().run_until_parked();
3189//     buffer.read_with(cx, |buffer, _| {
3190//         assert!(buffer.has_conflict());
3191//     });
3192// }
3193
3194// #[gpui::test]
3195// async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
3196//     init_test(cx);
3197
3198//     let fs = FakeFs::new(cx.background());
3199//     fs.insert_tree(
3200//         "/dir",
3201//         json!({
3202//             "file1": "a\nb\nc\n",
3203//             "file2": "one\r\ntwo\r\nthree\r\n",
3204//         }),
3205//     )
3206//     .await;
3207
3208//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3209//     let buffer1 = project
3210//         .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
3211//         .await
3212//         .unwrap();
3213//     let buffer2 = project
3214//         .update(cx, |p, cx| p.open_local_buffer("/dir/file2", cx))
3215//         .await
3216//         .unwrap();
3217
3218//     buffer1.read_with(cx, |buffer, _| {
3219//         assert_eq!(buffer.text(), "a\nb\nc\n");
3220//         assert_eq!(buffer.line_ending(), LineEnding::Unix);
3221//     });
3222//     buffer2.read_with(cx, |buffer, _| {
3223//         assert_eq!(buffer.text(), "one\ntwo\nthree\n");
3224//         assert_eq!(buffer.line_ending(), LineEnding::Windows);
3225//     });
3226
3227//     // Change a file's line endings on disk from unix to windows. The buffer's
3228//     // state updates correctly.
3229//     fs.save(
3230//         "/dir/file1".as_ref(),
3231//         &"aaa\nb\nc\n".into(),
3232//         LineEnding::Windows,
3233//     )
3234//     .await
3235//     .unwrap();
3236//     cx.foreground().run_until_parked();
3237//     buffer1.read_with(cx, |buffer, _| {
3238//         assert_eq!(buffer.text(), "aaa\nb\nc\n");
3239//         assert_eq!(buffer.line_ending(), LineEnding::Windows);
3240//     });
3241
3242//     // Save a file with windows line endings. The file is written correctly.
3243//     buffer2.update(cx, |buffer, cx| {
3244//         buffer.set_text("one\ntwo\nthree\nfour\n", cx);
3245//     });
3246//     project
3247//         .update(cx, |project, cx| project.save_buffer(buffer2, cx))
3248//         .await
3249//         .unwrap();
3250//     assert_eq!(
3251//         fs.load("/dir/file2".as_ref()).await.unwrap(),
3252//         "one\r\ntwo\r\nthree\r\nfour\r\n",
3253//     );
3254// }
3255
3256// #[gpui::test]
3257// async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
3258//     init_test(cx);
3259
3260//     let fs = FakeFs::new(cx.background());
3261//     fs.insert_tree(
3262//         "/the-dir",
3263//         json!({
3264//             "a.rs": "
3265//                 fn foo(mut v: Vec<usize>) {
3266//                     for x in &v {
3267//                         v.push(1);
3268//                     }
3269//                 }
3270//             "
3271//             .unindent(),
3272//         }),
3273//     )
3274//     .await;
3275
3276//     let project = Project::test(fs.clone(), ["/the-dir".as_ref()], cx).await;
3277//     let buffer = project
3278//         .update(cx, |p, cx| p.open_local_buffer("/the-dir/a.rs", cx))
3279//         .await
3280//         .unwrap();
3281
3282//     let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap();
3283//     let message = lsp::PublishDiagnosticsParams {
3284//         uri: buffer_uri.clone(),
3285//         diagnostics: vec![
3286//             lsp2::Diagnostic {
3287//                 range: lsp2::Range::new(lsp2::Position::new(1, 8), lsp2::Position::new(1, 9)),
3288//                 severity: Some(DiagnosticSeverity::WARNING),
3289//                 message: "error 1".to_string(),
3290//                 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3291//                     location: lsp::Location {
3292//                         uri: buffer_uri.clone(),
3293//                         range: lsp2::Range::new(
3294//                             lsp2::Position::new(1, 8),
3295//                             lsp2::Position::new(1, 9),
3296//                         ),
3297//                     },
3298//                     message: "error 1 hint 1".to_string(),
3299//                 }]),
3300//                 ..Default::default()
3301//             },
3302//             lsp2::Diagnostic {
3303//                 range: lsp2::Range::new(lsp2::Position::new(1, 8), lsp2::Position::new(1, 9)),
3304//                 severity: Some(DiagnosticSeverity::HINT),
3305//                 message: "error 1 hint 1".to_string(),
3306//                 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3307//                     location: lsp::Location {
3308//                         uri: buffer_uri.clone(),
3309//                         range: lsp2::Range::new(
3310//                             lsp2::Position::new(1, 8),
3311//                             lsp2::Position::new(1, 9),
3312//                         ),
3313//                     },
3314//                     message: "original diagnostic".to_string(),
3315//                 }]),
3316//                 ..Default::default()
3317//             },
3318//             lsp2::Diagnostic {
3319//                 range: lsp2::Range::new(lsp2::Position::new(2, 8), lsp2::Position::new(2, 17)),
3320//                 severity: Some(DiagnosticSeverity::ERROR),
3321//                 message: "error 2".to_string(),
3322//                 related_information: Some(vec![
3323//                     lsp::DiagnosticRelatedInformation {
3324//                         location: lsp::Location {
3325//                             uri: buffer_uri.clone(),
3326//                             range: lsp2::Range::new(
3327//                                 lsp2::Position::new(1, 13),
3328//                                 lsp2::Position::new(1, 15),
3329//                             ),
3330//                         },
3331//                         message: "error 2 hint 1".to_string(),
3332//                     },
3333//                     lsp::DiagnosticRelatedInformation {
3334//                         location: lsp::Location {
3335//                             uri: buffer_uri.clone(),
3336//                             range: lsp2::Range::new(
3337//                                 lsp2::Position::new(1, 13),
3338//                                 lsp2::Position::new(1, 15),
3339//                             ),
3340//                         },
3341//                         message: "error 2 hint 2".to_string(),
3342//                     },
3343//                 ]),
3344//                 ..Default::default()
3345//             },
3346//             lsp2::Diagnostic {
3347//                 range: lsp2::Range::new(lsp2::Position::new(1, 13), lsp2::Position::new(1, 15)),
3348//                 severity: Some(DiagnosticSeverity::HINT),
3349//                 message: "error 2 hint 1".to_string(),
3350//                 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3351//                     location: lsp::Location {
3352//                         uri: buffer_uri.clone(),
3353//                         range: lsp2::Range::new(
3354//                             lsp2::Position::new(2, 8),
3355//                             lsp2::Position::new(2, 17),
3356//                         ),
3357//                     },
3358//                     message: "original diagnostic".to_string(),
3359//                 }]),
3360//                 ..Default::default()
3361//             },
3362//             lsp2::Diagnostic {
3363//                 range: lsp2::Range::new(lsp2::Position::new(1, 13), lsp2::Position::new(1, 15)),
3364//                 severity: Some(DiagnosticSeverity::HINT),
3365//                 message: "error 2 hint 2".to_string(),
3366//                 related_information: Some(vec![lsp::DiagnosticRelatedInformation {
3367//                     location: lsp::Location {
3368//                         uri: buffer_uri,
3369//                         range: lsp2::Range::new(
3370//                             lsp2::Position::new(2, 8),
3371//                             lsp2::Position::new(2, 17),
3372//                         ),
3373//                     },
3374//                     message: "original diagnostic".to_string(),
3375//                 }]),
3376//                 ..Default::default()
3377//             },
3378//         ],
3379//         version: None,
3380//     };
3381
3382//     project
3383//         .update(cx, |p, cx| {
3384//             p.update_diagnostics(LanguageServerId(0), message, &[], cx)
3385//         })
3386//         .unwrap();
3387//     let buffer = buffer.read_with(cx, |buffer, _| buffer.snapshot());
3388
3389//     assert_eq!(
3390//         buffer
3391//             .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
3392//             .collect::<Vec<_>>(),
3393//         &[
3394//             DiagnosticEntry {
3395//                 range: Point::new(1, 8)..Point::new(1, 9),
3396//                 diagnostic: Diagnostic {
3397//                     severity: DiagnosticSeverity::WARNING,
3398//                     message: "error 1".to_string(),
3399//                     group_id: 1,
3400//                     is_primary: true,
3401//                     ..Default::default()
3402//                 }
3403//             },
3404//             DiagnosticEntry {
3405//                 range: Point::new(1, 8)..Point::new(1, 9),
3406//                 diagnostic: Diagnostic {
3407//                     severity: DiagnosticSeverity::HINT,
3408//                     message: "error 1 hint 1".to_string(),
3409//                     group_id: 1,
3410//                     is_primary: false,
3411//                     ..Default::default()
3412//                 }
3413//             },
3414//             DiagnosticEntry {
3415//                 range: Point::new(1, 13)..Point::new(1, 15),
3416//                 diagnostic: Diagnostic {
3417//                     severity: DiagnosticSeverity::HINT,
3418//                     message: "error 2 hint 1".to_string(),
3419//                     group_id: 0,
3420//                     is_primary: false,
3421//                     ..Default::default()
3422//                 }
3423//             },
3424//             DiagnosticEntry {
3425//                 range: Point::new(1, 13)..Point::new(1, 15),
3426//                 diagnostic: Diagnostic {
3427//                     severity: DiagnosticSeverity::HINT,
3428//                     message: "error 2 hint 2".to_string(),
3429//                     group_id: 0,
3430//                     is_primary: false,
3431//                     ..Default::default()
3432//                 }
3433//             },
3434//             DiagnosticEntry {
3435//                 range: Point::new(2, 8)..Point::new(2, 17),
3436//                 diagnostic: Diagnostic {
3437//                     severity: DiagnosticSeverity::ERROR,
3438//                     message: "error 2".to_string(),
3439//                     group_id: 0,
3440//                     is_primary: true,
3441//                     ..Default::default()
3442//                 }
3443//             }
3444//         ]
3445//     );
3446
3447//     assert_eq!(
3448//         buffer.diagnostic_group::<Point>(0).collect::<Vec<_>>(),
3449//         &[
3450//             DiagnosticEntry {
3451//                 range: Point::new(1, 13)..Point::new(1, 15),
3452//                 diagnostic: Diagnostic {
3453//                     severity: DiagnosticSeverity::HINT,
3454//                     message: "error 2 hint 1".to_string(),
3455//                     group_id: 0,
3456//                     is_primary: false,
3457//                     ..Default::default()
3458//                 }
3459//             },
3460//             DiagnosticEntry {
3461//                 range: Point::new(1, 13)..Point::new(1, 15),
3462//                 diagnostic: Diagnostic {
3463//                     severity: DiagnosticSeverity::HINT,
3464//                     message: "error 2 hint 2".to_string(),
3465//                     group_id: 0,
3466//                     is_primary: false,
3467//                     ..Default::default()
3468//                 }
3469//             },
3470//             DiagnosticEntry {
3471//                 range: Point::new(2, 8)..Point::new(2, 17),
3472//                 diagnostic: Diagnostic {
3473//                     severity: DiagnosticSeverity::ERROR,
3474//                     message: "error 2".to_string(),
3475//                     group_id: 0,
3476//                     is_primary: true,
3477//                     ..Default::default()
3478//                 }
3479//             }
3480//         ]
3481//     );
3482
3483//     assert_eq!(
3484//         buffer.diagnostic_group::<Point>(1).collect::<Vec<_>>(),
3485//         &[
3486//             DiagnosticEntry {
3487//                 range: Point::new(1, 8)..Point::new(1, 9),
3488//                 diagnostic: Diagnostic {
3489//                     severity: DiagnosticSeverity::WARNING,
3490//                     message: "error 1".to_string(),
3491//                     group_id: 1,
3492//                     is_primary: true,
3493//                     ..Default::default()
3494//                 }
3495//             },
3496//             DiagnosticEntry {
3497//                 range: Point::new(1, 8)..Point::new(1, 9),
3498//                 diagnostic: Diagnostic {
3499//                     severity: DiagnosticSeverity::HINT,
3500//                     message: "error 1 hint 1".to_string(),
3501//                     group_id: 1,
3502//                     is_primary: false,
3503//                     ..Default::default()
3504//                 }
3505//             },
3506//         ]
3507//     );
3508// }
3509
3510// #[gpui::test]
3511// async fn test_rename(cx: &mut gpui::TestAppContext) {
3512//     init_test(cx);
3513
3514//     let mut language = Language::new(
3515//         LanguageConfig {
3516//             name: "Rust".into(),
3517//             path_suffixes: vec!["rs".to_string()],
3518//             ..Default::default()
3519//         },
3520//         Some(tree_sitter_rust::language()),
3521//     );
3522//     let mut fake_servers = language
3523//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3524//             capabilities: lsp2::ServerCapabilities {
3525//                 rename_provider: Some(lsp2::OneOf::Right(lsp2::RenameOptions {
3526//                     prepare_provider: Some(true),
3527//                     work_done_progress_options: Default::default(),
3528//                 })),
3529//                 ..Default::default()
3530//             },
3531//             ..Default::default()
3532//         }))
3533//         .await;
3534
3535//     let fs = FakeFs::new(cx.background());
3536//     fs.insert_tree(
3537//         "/dir",
3538//         json!({
3539//             "one.rs": "const ONE: usize = 1;",
3540//             "two.rs": "const TWO: usize = one::ONE + one::ONE;"
3541//         }),
3542//     )
3543//     .await;
3544
3545//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3546//     project.update(cx, |project, _| project.languages.add(Arc::new(language)));
3547//     let buffer = project
3548//         .update(cx, |project, cx| {
3549//             project.open_local_buffer("/dir/one.rs", cx)
3550//         })
3551//         .await
3552//         .unwrap();
3553
3554//     let fake_server = fake_servers.next().await.unwrap();
3555
3556//     let response = project.update(cx, |project, cx| {
3557//         project.prepare_rename(buffer.clone(), 7, cx)
3558//     });
3559//     fake_server
3560//         .handle_request::<lsp2::request::PrepareRenameRequest, _, _>(|params, _| async move {
3561//             assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
3562//             assert_eq!(params.position, lsp2::Position::new(0, 7));
3563//             Ok(Some(lsp2::PrepareRenameResponse::Range(lsp2::Range::new(
3564//                 lsp2::Position::new(0, 6),
3565//                 lsp2::Position::new(0, 9),
3566//             ))))
3567//         })
3568//         .next()
3569//         .await
3570//         .unwrap();
3571//     let range = response.await.unwrap().unwrap();
3572//     let range = buffer.read_with(cx, |buffer, _| range.to_offset(buffer));
3573//     assert_eq!(range, 6..9);
3574
3575//     let response = project.update(cx, |project, cx| {
3576//         project.perform_rename(buffer.clone(), 7, "THREE".to_string(), true, cx)
3577//     });
3578//     fake_server
3579//         .handle_request::<lsp2::request::Rename, _, _>(|params, _| async move {
3580//             assert_eq!(
3581//                 params.text_document_position.text_document.uri.as_str(),
3582//                 "file:///dir/one.rs"
3583//             );
3584//             assert_eq!(
3585//                 params.text_document_position.position,
3586//                 lsp2::Position::new(0, 7)
3587//             );
3588//             assert_eq!(params.new_name, "THREE");
3589//             Ok(Some(lsp::WorkspaceEdit {
3590//                 changes: Some(
3591//                     [
3592//                         (
3593//                             lsp2::Url::from_file_path("/dir/one.rs").unwrap(),
3594//                             vec![lsp2::TextEdit::new(
3595//                                 lsp2::Range::new(
3596//                                     lsp2::Position::new(0, 6),
3597//                                     lsp2::Position::new(0, 9),
3598//                                 ),
3599//                                 "THREE".to_string(),
3600//                             )],
3601//                         ),
3602//                         (
3603//                             lsp2::Url::from_file_path("/dir/two.rs").unwrap(),
3604//                             vec![
3605//                                 lsp2::TextEdit::new(
3606//                                     lsp2::Range::new(
3607//                                         lsp2::Position::new(0, 24),
3608//                                         lsp2::Position::new(0, 27),
3609//                                     ),
3610//                                     "THREE".to_string(),
3611//                                 ),
3612//                                 lsp2::TextEdit::new(
3613//                                     lsp2::Range::new(
3614//                                         lsp2::Position::new(0, 35),
3615//                                         lsp2::Position::new(0, 38),
3616//                                     ),
3617//                                     "THREE".to_string(),
3618//                                 ),
3619//                             ],
3620//                         ),
3621//                     ]
3622//                     .into_iter()
3623//                     .collect(),
3624//                 ),
3625//                 ..Default::default()
3626//             }))
3627//         })
3628//         .next()
3629//         .await
3630//         .unwrap();
3631//     let mut transaction = response.await.unwrap().0;
3632//     assert_eq!(transaction.len(), 2);
3633//     assert_eq!(
3634//         transaction
3635//             .remove_entry(&buffer)
3636//             .unwrap()
3637//             .0
3638//             .read_with(cx, |buffer, _| buffer.text()),
3639//         "const THREE: usize = 1;"
3640//     );
3641//     assert_eq!(
3642//         transaction
3643//             .into_keys()
3644//             .next()
3645//             .unwrap()
3646//             .read_with(cx, |buffer, _| buffer.text()),
3647//         "const TWO: usize = one::THREE + one::THREE;"
3648//     );
3649// }
3650
3651// #[gpui::test]
3652// async fn test_search(cx: &mut gpui::TestAppContext) {
3653//     init_test(cx);
3654
3655//     let fs = FakeFs::new(cx.background());
3656//     fs.insert_tree(
3657//         "/dir",
3658//         json!({
3659//             "one.rs": "const ONE: usize = 1;",
3660//             "two.rs": "const TWO: usize = one::ONE + one::ONE;",
3661//             "three.rs": "const THREE: usize = one::ONE + two::TWO;",
3662//             "four.rs": "const FOUR: usize = one::ONE + three::THREE;",
3663//         }),
3664//     )
3665//     .await;
3666//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3667//     assert_eq!(
3668//         search(
3669//             &project,
3670//             SearchQuery::text("TWO", false, true, Vec::new(), Vec::new()).unwrap(),
3671//             cx
3672//         )
3673//         .await
3674//         .unwrap(),
3675//         HashMap::from_iter([
3676//             ("two.rs".to_string(), vec![6..9]),
3677//             ("three.rs".to_string(), vec![37..40])
3678//         ])
3679//     );
3680
3681//     let buffer_4 = project
3682//         .update(cx, |project, cx| {
3683//             project.open_local_buffer("/dir/four.rs", cx)
3684//         })
3685//         .await
3686//         .unwrap();
3687//     buffer_4.update(cx, |buffer, cx| {
3688//         let text = "two::TWO";
3689//         buffer.edit([(20..28, text), (31..43, text)], None, cx);
3690//     });
3691
3692//     assert_eq!(
3693//         search(
3694//             &project,
3695//             SearchQuery::text("TWO", false, true, Vec::new(), Vec::new()).unwrap(),
3696//             cx
3697//         )
3698//         .await
3699//         .unwrap(),
3700//         HashMap::from_iter([
3701//             ("two.rs".to_string(), vec![6..9]),
3702//             ("three.rs".to_string(), vec![37..40]),
3703//             ("four.rs".to_string(), vec![25..28, 36..39])
3704//         ])
3705//     );
3706// }
3707
3708// #[gpui::test]
3709// async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
3710//     init_test(cx);
3711
3712//     let search_query = "file";
3713
3714//     let fs = FakeFs::new(cx.background());
3715//     fs.insert_tree(
3716//         "/dir",
3717//         json!({
3718//             "one.rs": r#"// Rust file one"#,
3719//             "one.ts": r#"// TypeScript file one"#,
3720//             "two.rs": r#"// Rust file two"#,
3721//             "two.ts": r#"// TypeScript file two"#,
3722//         }),
3723//     )
3724//     .await;
3725//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3726
3727//     assert!(
3728//         search(
3729//             &project,
3730//             SearchQuery::text(
3731//                 search_query,
3732//                 false,
3733//                 true,
3734//                 vec![PathMatcher::new("*.odd").unwrap()],
3735//                 Vec::new()
3736//             )
3737//             .unwrap(),
3738//             cx
3739//         )
3740//         .await
3741//         .unwrap()
3742//         .is_empty(),
3743//         "If no inclusions match, no files should be returned"
3744//     );
3745
3746//     assert_eq!(
3747//         search(
3748//             &project,
3749//             SearchQuery::text(
3750//                 search_query,
3751//                 false,
3752//                 true,
3753//                 vec![PathMatcher::new("*.rs").unwrap()],
3754//                 Vec::new()
3755//             )
3756//             .unwrap(),
3757//             cx
3758//         )
3759//         .await
3760//         .unwrap(),
3761//         HashMap::from_iter([
3762//             ("one.rs".to_string(), vec![8..12]),
3763//             ("two.rs".to_string(), vec![8..12]),
3764//         ]),
3765//         "Rust only search should give only Rust files"
3766//     );
3767
3768//     assert_eq!(
3769//         search(
3770//             &project,
3771//             SearchQuery::text(
3772//                 search_query,
3773//                 false,
3774//                 true,
3775//                 vec![
3776//                     PathMatcher::new("*.ts").unwrap(),
3777//                     PathMatcher::new("*.odd").unwrap(),
3778//                 ],
3779//                 Vec::new()
3780//             ).unwrap(),
3781//             cx
3782//         )
3783//         .await
3784//         .unwrap(),
3785//         HashMap::from_iter([
3786//             ("one.ts".to_string(), vec![14..18]),
3787//             ("two.ts".to_string(), vec![14..18]),
3788//         ]),
3789//         "TypeScript only search should give only TypeScript files, even if other inclusions don't match anything"
3790//     );
3791
3792//     assert_eq!(
3793//         search(
3794//             &project,
3795//             SearchQuery::text(
3796//                 search_query,
3797//                 false,
3798//                 true,
3799//                 vec![
3800//                     PathMatcher::new("*.rs").unwrap(),
3801//                     PathMatcher::new("*.ts").unwrap(),
3802//                     PathMatcher::new("*.odd").unwrap(),
3803//                 ],
3804//                 Vec::new()
3805//             ).unwrap(),
3806//             cx
3807//         )
3808//         .await
3809//         .unwrap(),
3810//         HashMap::from_iter([
3811//             ("one.rs".to_string(), vec![8..12]),
3812//             ("one.ts".to_string(), vec![14..18]),
3813//             ("two.rs".to_string(), vec![8..12]),
3814//             ("two.ts".to_string(), vec![14..18]),
3815//         ]),
3816//         "Rust and typescript search should give both Rust and TypeScript files, even if other inclusions don't match anything"
3817//     );
3818// }
3819
3820// #[gpui::test]
3821// async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
3822//     init_test(cx);
3823
3824//     let search_query = "file";
3825
3826//     let fs = FakeFs::new(cx.background());
3827//     fs.insert_tree(
3828//         "/dir",
3829//         json!({
3830//             "one.rs": r#"// Rust file one"#,
3831//             "one.ts": r#"// TypeScript file one"#,
3832//             "two.rs": r#"// Rust file two"#,
3833//             "two.ts": r#"// TypeScript file two"#,
3834//         }),
3835//     )
3836//     .await;
3837//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3838
3839//     assert_eq!(
3840//         search(
3841//             &project,
3842//             SearchQuery::text(
3843//                 search_query,
3844//                 false,
3845//                 true,
3846//                 Vec::new(),
3847//                 vec![PathMatcher::new("*.odd").unwrap()],
3848//             )
3849//             .unwrap(),
3850//             cx
3851//         )
3852//         .await
3853//         .unwrap(),
3854//         HashMap::from_iter([
3855//             ("one.rs".to_string(), vec![8..12]),
3856//             ("one.ts".to_string(), vec![14..18]),
3857//             ("two.rs".to_string(), vec![8..12]),
3858//             ("two.ts".to_string(), vec![14..18]),
3859//         ]),
3860//         "If no exclusions match, all files should be returned"
3861//     );
3862
3863//     assert_eq!(
3864//         search(
3865//             &project,
3866//             SearchQuery::text(
3867//                 search_query,
3868//                 false,
3869//                 true,
3870//                 Vec::new(),
3871//                 vec![PathMatcher::new("*.rs").unwrap()],
3872//             )
3873//             .unwrap(),
3874//             cx
3875//         )
3876//         .await
3877//         .unwrap(),
3878//         HashMap::from_iter([
3879//             ("one.ts".to_string(), vec![14..18]),
3880//             ("two.ts".to_string(), vec![14..18]),
3881//         ]),
3882//         "Rust exclusion search should give only TypeScript files"
3883//     );
3884
3885//     assert_eq!(
3886//         search(
3887//             &project,
3888//             SearchQuery::text(
3889//                 search_query,
3890//                 false,
3891//                 true,
3892//                 Vec::new(),
3893//                 vec![
3894//                     PathMatcher::new("*.ts").unwrap(),
3895//                     PathMatcher::new("*.odd").unwrap(),
3896//                 ],
3897//             ).unwrap(),
3898//             cx
3899//         )
3900//         .await
3901//         .unwrap(),
3902//         HashMap::from_iter([
3903//             ("one.rs".to_string(), vec![8..12]),
3904//             ("two.rs".to_string(), vec![8..12]),
3905//         ]),
3906//         "TypeScript exclusion search should give only Rust files, even if other exclusions don't match anything"
3907//     );
3908
3909//     assert!(
3910//         search(
3911//             &project,
3912//             SearchQuery::text(
3913//                 search_query,
3914//                 false,
3915//                 true,
3916//                 Vec::new(),
3917//                 vec![
3918//                     PathMatcher::new("*.rs").unwrap(),
3919//                     PathMatcher::new("*.ts").unwrap(),
3920//                     PathMatcher::new("*.odd").unwrap(),
3921//                 ],
3922//             ).unwrap(),
3923//             cx
3924//         )
3925//         .await
3926//         .unwrap().is_empty(),
3927//         "Rust and typescript exclusion should give no files, even if other exclusions don't match anything"
3928//     );
3929// }
3930
3931// #[gpui::test]
3932// async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContext) {
3933//     init_test(cx);
3934
3935//     let search_query = "file";
3936
3937//     let fs = FakeFs::new(cx.background());
3938//     fs.insert_tree(
3939//         "/dir",
3940//         json!({
3941//             "one.rs": r#"// Rust file one"#,
3942//             "one.ts": r#"// TypeScript file one"#,
3943//             "two.rs": r#"// Rust file two"#,
3944//             "two.ts": r#"// TypeScript file two"#,
3945//         }),
3946//     )
3947//     .await;
3948//     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
3949
3950//     assert!(
3951//         search(
3952//             &project,
3953//             SearchQuery::text(
3954//                 search_query,
3955//                 false,
3956//                 true,
3957//                 vec![PathMatcher::new("*.odd").unwrap()],
3958//                 vec![PathMatcher::new("*.odd").unwrap()],
3959//             )
3960//             .unwrap(),
3961//             cx
3962//         )
3963//         .await
3964//         .unwrap()
3965//         .is_empty(),
3966//         "If both no exclusions and inclusions match, exclusions should win and return nothing"
3967//     );
3968
3969//     assert!(
3970//         search(
3971//             &project,
3972//             SearchQuery::text(
3973//                 search_query,
3974//                 false,
3975//                 true,
3976//                 vec![PathMatcher::new("*.ts").unwrap()],
3977//                 vec![PathMatcher::new("*.ts").unwrap()],
3978//             ).unwrap(),
3979//             cx
3980//         )
3981//         .await
3982//         .unwrap()
3983//         .is_empty(),
3984//         "If both TypeScript exclusions and inclusions match, exclusions should win and return nothing files."
3985//     );
3986
3987//     assert!(
3988//         search(
3989//             &project,
3990//             SearchQuery::text(
3991//                 search_query,
3992//                 false,
3993//                 true,
3994//                 vec![
3995//                     PathMatcher::new("*.ts").unwrap(),
3996//                     PathMatcher::new("*.odd").unwrap()
3997//                 ],
3998//                 vec![
3999//                     PathMatcher::new("*.ts").unwrap(),
4000//                     PathMatcher::new("*.odd").unwrap()
4001//                 ],
4002//             )
4003//             .unwrap(),
4004//             cx
4005//         )
4006//         .await
4007//         .unwrap()
4008//         .is_empty(),
4009//         "Non-matching inclusions and exclusions should not change that."
4010//     );
4011
4012//     assert_eq!(
4013//         search(
4014//             &project,
4015//             SearchQuery::text(
4016//                 search_query,
4017//                 false,
4018//                 true,
4019//                 vec![
4020//                     PathMatcher::new("*.ts").unwrap(),
4021//                     PathMatcher::new("*.odd").unwrap()
4022//                 ],
4023//                 vec![
4024//                     PathMatcher::new("*.rs").unwrap(),
4025//                     PathMatcher::new("*.odd").unwrap()
4026//                 ],
4027//             )
4028//             .unwrap(),
4029//             cx
4030//         )
4031//         .await
4032//         .unwrap(),
4033//         HashMap::from_iter([
4034//             ("one.ts".to_string(), vec![14..18]),
4035//             ("two.ts".to_string(), vec![14..18]),
4036//         ]),
4037//         "Non-intersecting TypeScript inclusions and Rust exclusions should return TypeScript files"
4038//     );
4039// }
4040
4041// #[test]
4042// fn test_glob_literal_prefix() {
4043//     assert_eq!(glob_literal_prefix("**/*.js"), "");
4044//     assert_eq!(glob_literal_prefix("node_modules/**/*.js"), "node_modules");
4045//     assert_eq!(glob_literal_prefix("foo/{bar,baz}.js"), "foo");
4046//     assert_eq!(glob_literal_prefix("foo/bar/baz.js"), "foo/bar/baz.js");
4047// }
4048
4049// async fn search(
4050//     project: &ModelHandle<Project>,
4051//     query: SearchQuery,
4052//     cx: &mut gpui::TestAppContext,
4053// ) -> Result<HashMap<String, Vec<Range<usize>>>> {
4054//     let mut search_rx = project.update(cx, |project, cx| project.search(query, cx));
4055//     let mut result = HashMap::default();
4056//     while let Some((buffer, range)) = search_rx.next().await {
4057//         result.entry(buffer).or_insert(range);
4058//     }
4059//     Ok(result
4060//         .into_iter()
4061//         .map(|(buffer, ranges)| {
4062//             buffer.read_with(cx, |buffer, _| {
4063//                 let path = buffer.file().unwrap().path().to_string_lossy().to_string();
4064//                 let ranges = ranges
4065//                     .into_iter()
4066//                     .map(|range| range.to_offset(buffer))
4067//                     .collect::<Vec<_>>();
4068//                 (path, ranges)
4069//             })
4070//         })
4071//         .collect())
4072// }
4073
4074// fn init_test(cx: &mut gpui::TestAppContext) {
4075//     cx.foreground().forbid_parking();
4076
4077//     cx.update(|cx| {
4078//         cx.set_global(SettingsStore::test(cx));
4079//         language2::init(cx);
4080//         Project::init_settings(cx);
4081//     });
4082// }