Fix assignment of language to formerly-untitled buffers

Max Brunsfeld and Nathan Sobo created

When lazy-loading a language, check if it matches plain text buffers.

Co-authored-by: Nathan Sobo <nathan@zed.dev>

Change summary

crates/project/src/project.rs       | 16 ++++++++++------
crates/project/src/project_tests.rs | 29 +++++++++++++++++++++++++----
2 files changed, 35 insertions(+), 10 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -554,11 +554,13 @@ impl Project {
             });
         }
 
-        let languages = Arc::new(LanguageRegistry::test());
+        let mut languages = LanguageRegistry::test();
+        languages.set_executor(cx.background());
         let http_client = client::test::FakeHttpClient::with_404_response();
         let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
         let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
-        let project = cx.update(|cx| Project::local(client, user_store, languages, fs, cx));
+        let project =
+            cx.update(|cx| Project::local(client, user_store, Arc::new(languages), fs, cx));
         for path in root_paths {
             let (tree, _) = project
                 .update(cx, |project, cx| {
@@ -1789,20 +1791,22 @@ impl Project {
             while let Some(()) = subscription.next().await {
                 if let Some(project) = project.upgrade(&cx) {
                     project.update(&mut cx, |project, cx| {
-                        let mut buffers_without_language = Vec::new();
+                        let mut plain_text_buffers = Vec::new();
                         let mut buffers_with_unknown_injections = Vec::new();
                         for buffer in project.opened_buffers.values() {
                             if let Some(handle) = buffer.upgrade(cx) {
                                 let buffer = &handle.read(cx);
-                                if buffer.language().is_none() {
-                                    buffers_without_language.push(handle);
+                                if buffer.language().is_none()
+                                    || buffer.language() == Some(&*language::PLAIN_TEXT)
+                                {
+                                    plain_text_buffers.push(handle);
                                 } else if buffer.contains_unknown_injections() {
                                     buffers_with_unknown_injections.push(handle);
                                 }
                             }
                         }
 
-                        for buffer in buffers_without_language {
+                        for buffer in plain_text_buffers {
                             project.assign_language_to_buffer(&buffer, cx);
                             project.register_buffer_with_language_server(&buffer, cx);
                         }

crates/project/src/project_tests.rs 🔗

@@ -2130,6 +2130,20 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
     fs.insert_tree("/dir", json!({})).await;
 
     let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
+
+    let languages = project.read_with(cx, |project, _| project.languages().clone());
+    languages.register(
+        "/some/path",
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".into()],
+            ..Default::default()
+        },
+        tree_sitter_rust::language(),
+        None,
+        |_| Default::default(),
+    );
+
     let buffer = project.update(cx, |project, cx| {
         project.create_buffer("", None, cx).unwrap()
     });
@@ -2137,23 +2151,30 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
         buffer.edit([(0..0, "abc")], None, cx);
         assert!(buffer.is_dirty());
         assert!(!buffer.has_conflict());
+        assert_eq!(buffer.language().unwrap().name().as_ref(), "Plain Text");
     });
     project
         .update(cx, |project, cx| {
-            project.save_buffer_as(buffer.clone(), "/dir/file1".into(), cx)
+            project.save_buffer_as(buffer.clone(), "/dir/file1.rs".into(), cx)
         })
         .await
         .unwrap();
-    assert_eq!(fs.load(Path::new("/dir/file1")).await.unwrap(), "abc");
+    assert_eq!(fs.load(Path::new("/dir/file1.rs")).await.unwrap(), "abc");
+
+    cx.foreground().run_until_parked();
     buffer.read_with(cx, |buffer, cx| {
-        assert_eq!(buffer.file().unwrap().full_path(cx), Path::new("dir/file1"));
+        assert_eq!(
+            buffer.file().unwrap().full_path(cx),
+            Path::new("dir/file1.rs")
+        );
         assert!(!buffer.is_dirty());
         assert!(!buffer.has_conflict());
+        assert_eq!(buffer.language().unwrap().name().as_ref(), "Rust");
     });
 
     let opened_buffer = project
         .update(cx, |project, cx| {
-            project.open_local_buffer("/dir/file1", cx)
+            project.open_local_buffer("/dir/file1.rs", cx)
         })
         .await
         .unwrap();