Allow languages to be registered at any time

Max Brunsfeld , Nathan Sobo , and Antonio Scandurra created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

crates/editor/src/display_map.rs   |   4 
crates/language/src/language.rs    |  58 ++++--
crates/language/src/tests.rs       |  14 
crates/project/src/project.rs      |  13 +
crates/server/src/rpc.rs           | 286 ++++++++++++++++---------------
crates/workspace/src/lsp_status.rs |   2 
crates/workspace/src/settings.rs   |  12 
crates/zed/src/test.rs             |  21 +-
crates/zed/src/zed.rs              |   4 
9 files changed, 222 insertions(+), 192 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -864,7 +864,7 @@ mod tests {
         let language = Arc::new(
             Language::new(
                 LanguageConfig {
-                    name: "Test".to_string(),
+                    name: "Test".into(),
                     path_suffixes: vec![".test".to_string()],
                     ..Default::default()
                 },
@@ -951,7 +951,7 @@ mod tests {
         let language = Arc::new(
             Language::new(
                 LanguageConfig {
-                    name: "Test".to_string(),
+                    name: "Test".into(),
                     path_suffixes: vec![".test".to_string()],
                     ..Default::default()
                 },

crates/language/src/language.rs 🔗

@@ -16,7 +16,7 @@ use futures::{
 use gpui::{AppContext, Task};
 use highlight_map::HighlightMap;
 use lazy_static::lazy_static;
-use parking_lot::Mutex;
+use parking_lot::{Mutex, RwLock};
 use serde::Deserialize;
 use std::{
     cell::RefCell,
@@ -45,7 +45,7 @@ thread_local! {
 lazy_static! {
     pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
         LanguageConfig {
-            name: "Plain Text".to_string(),
+            name: "Plain Text".into(),
             path_suffixes: Default::default(),
             brackets: Default::default(),
             line_comment: None,
@@ -92,15 +92,27 @@ pub struct CodeLabel {
     pub filter_range: Range<usize>,
 }
 
-#[derive(Default, Deserialize)]
+#[derive(Deserialize)]
 pub struct LanguageConfig {
-    pub name: String,
+    pub name: Arc<str>,
     pub path_suffixes: Vec<String>,
     pub brackets: Vec<BracketPair>,
     pub line_comment: Option<String>,
     pub language_server: Option<LanguageServerConfig>,
 }
 
+impl Default for LanguageConfig {
+    fn default() -> Self {
+        Self {
+            name: "".into(),
+            path_suffixes: Default::default(),
+            brackets: Default::default(),
+            line_comment: Default::default(),
+            language_server: Default::default(),
+        }
+    }
+}
+
 #[derive(Default, Deserialize)]
 pub struct LanguageServerConfig {
     pub disk_based_diagnostic_sources: HashSet<String>,
@@ -151,7 +163,7 @@ pub enum LanguageServerBinaryStatus {
 }
 
 pub struct LanguageRegistry {
-    languages: Vec<Arc<Language>>,
+    languages: RwLock<Vec<Arc<Language>>>,
     language_server_download_dir: Option<Arc<Path>>,
     lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
     lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>,
@@ -168,12 +180,12 @@ impl LanguageRegistry {
         }
     }
 
-    pub fn add(&mut self, language: Arc<Language>) {
-        self.languages.push(language.clone());
+    pub fn add(&self, language: Arc<Language>) {
+        self.languages.write().push(language.clone());
     }
 
     pub fn set_theme(&self, theme: &SyntaxTheme) {
-        for language in &self.languages {
+        for language in self.languages.read().iter() {
             language.set_theme(theme);
         }
     }
@@ -182,24 +194,30 @@ impl LanguageRegistry {
         self.language_server_download_dir = Some(path.into());
     }
 
-    pub fn get_language(&self, name: &str) -> Option<&Arc<Language>> {
+    pub fn get_language(&self, name: &str) -> Option<Arc<Language>> {
         self.languages
+            .read()
             .iter()
-            .find(|language| language.name() == name)
+            .find(|language| language.name().as_ref() == name)
+            .cloned()
     }
 
-    pub fn select_language(&self, path: impl AsRef<Path>) -> Option<&Arc<Language>> {
+    pub fn select_language(&self, path: impl AsRef<Path>) -> Option<Arc<Language>> {
         let path = path.as_ref();
         let filename = path.file_name().and_then(|name| name.to_str());
         let extension = path.extension().and_then(|name| name.to_str());
         let path_suffixes = [extension, filename];
-        self.languages.iter().find(|language| {
-            language
-                .config
-                .path_suffixes
-                .iter()
-                .any(|suffix| path_suffixes.contains(&Some(suffix.as_str())))
-        })
+        self.languages
+            .read()
+            .iter()
+            .find(|language| {
+                language
+                    .config
+                    .path_suffixes
+                    .iter()
+                    .any(|suffix| path_suffixes.contains(&Some(suffix.as_str())))
+            })
+            .cloned()
     }
 
     pub fn start_language_server(
@@ -401,8 +419,8 @@ impl Language {
         self
     }
 
-    pub fn name(&self) -> &str {
-        self.config.name.as_str()
+    pub fn name(&self) -> Arc<str> {
+        self.config.name.clone()
     }
 
     pub fn line_comment_prefix(&self) -> Option<&str> {

crates/language/src/tests.rs 🔗

@@ -24,10 +24,10 @@ fn init_logger() {
 
 #[gpui::test]
 fn test_select_language() {
-    let mut registry = LanguageRegistry::new();
+    let registry = LanguageRegistry::new();
     registry.add(Arc::new(Language::new(
         LanguageConfig {
-            name: "Rust".to_string(),
+            name: "Rust".into(),
             path_suffixes: vec!["rs".to_string()],
             ..Default::default()
         },
@@ -35,7 +35,7 @@ fn test_select_language() {
     )));
     registry.add(Arc::new(Language::new(
         LanguageConfig {
-            name: "Make".to_string(),
+            name: "Make".into(),
             path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
             ..Default::default()
         },
@@ -45,17 +45,17 @@ fn test_select_language() {
     // matching file extension
     assert_eq!(
         registry.select_language("zed/lib.rs").map(|l| l.name()),
-        Some("Rust")
+        Some("Rust".into())
     );
     assert_eq!(
         registry.select_language("zed/lib.mk").map(|l| l.name()),
-        Some("Make")
+        Some("Make".into())
     );
 
     // matching filename
     assert_eq!(
         registry.select_language("zed/Makefile").map(|l| l.name()),
-        Some("Make")
+        Some("Make".into())
     );
 
     // matching suffix that is not the full file extension or filename
@@ -1354,7 +1354,7 @@ impl Buffer {
 fn rust_lang() -> Language {
     Language::new(
         LanguageConfig {
-            name: "Rust".to_string(),
+            name: "Rust".into(),
             path_suffixes: vec!["rs".to_string()],
             language_server: None,
             ..Default::default()

crates/project/src/project.rs 🔗

@@ -387,6 +387,11 @@ impl Project {
             .any(|buffer| matches!(buffer, OpenBuffer::Loading(_)))
     }
 
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn languages(&self) -> &Arc<LanguageRegistry> {
+        &self.languages
+    }
+
     pub fn fs(&self) -> &Arc<dyn Fs> {
         &self.fs
     }
@@ -817,7 +822,7 @@ impl Project {
         };
 
         // If the buffer has a language, set it and start/assign the language server
-        if let Some(language) = self.languages.select_language(&full_path).cloned() {
+        if let Some(language) = self.languages.select_language(&full_path) {
             buffer.update(cx, |buffer, cx| {
                 buffer.set_language(Some(language.clone()), cx);
             });
@@ -3386,7 +3391,7 @@ mod tests {
 
         let language = Arc::new(Language::new(
             LanguageConfig {
-                name: "Rust".to_string(),
+                name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
                 language_server: Some(language_server_config),
                 ..Default::default()
@@ -3532,7 +3537,7 @@ mod tests {
         let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
         let language = Arc::new(Language::new(
             LanguageConfig {
-                name: "Rust".to_string(),
+                name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
                 language_server: Some(language_server_config),
                 ..Default::default()
@@ -4425,7 +4430,7 @@ mod tests {
         let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
         let language = Arc::new(Language::new(
             LanguageConfig {
-                name: "Rust".to_string(),
+                name: "Rust".into(),
                 path_suffixes: vec!["rs".to_string()],
                 language_server: Some(language_server_config),
                 ..Default::default()

crates/server/src/rpc.rs 🔗

@@ -2035,7 +2035,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -2266,7 +2266,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -2468,7 +2468,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -2585,7 +2585,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -2733,7 +2733,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -2834,7 +2834,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -3073,7 +3073,7 @@ mod tests {
             .unwrap()
             .add(Arc::new(Language::new(
                 LanguageConfig {
-                    name: "Rust".to_string(),
+                    name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
                     language_server: Some(language_server_config),
                     ..Default::default()
@@ -3842,50 +3842,8 @@ mod tests {
 
         let rng = Rc::new(RefCell::new(rng));
 
-        let mut host_lang_registry = Arc::new(LanguageRegistry::new());
         let guest_lang_registry = Arc::new(LanguageRegistry::new());
-
-        // Set up a fake language server.
-        let (mut language_server_config, _fake_language_servers) = LanguageServerConfig::fake();
-        language_server_config.set_fake_initializer(|fake_server| {
-            fake_server.handle_request::<lsp::request::Completion, _>(|_| {
-                Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem {
-                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-                        range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
-                        new_text: "the-new-text".to_string(),
-                    })),
-                    ..Default::default()
-                }]))
-            });
-
-            fake_server.handle_request::<lsp::request::CodeActionRequest, _>(|_| {
-                Some(vec![lsp::CodeActionOrCommand::CodeAction(
-                    lsp::CodeAction {
-                        title: "the-code-action".to_string(),
-                        ..Default::default()
-                    },
-                )])
-            });
-
-            fake_server.handle_request::<lsp::request::PrepareRenameRequest, _>(|params| {
-                Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
-                    params.position,
-                    params.position,
-                )))
-            });
-        });
-
-        Arc::get_mut(&mut host_lang_registry)
-            .unwrap()
-            .add(Arc::new(Language::new(
-                LanguageConfig {
-                    name: "Rust".to_string(),
-                    path_suffixes: vec!["rs".to_string()],
-                    language_server: Some(language_server_config),
-                    ..Default::default()
-                },
-                None,
-            )));
+        let (language_server_config, _fake_language_servers) = LanguageServerConfig::fake();
 
         let fs = FakeFs::new(cx.background());
         fs.insert_tree(
@@ -3914,7 +3872,7 @@ mod tests {
             Project::local(
                 host.client.clone(),
                 host.user_store.clone(),
-                host_lang_registry.clone(),
+                Arc::new(LanguageRegistry::new()),
                 fs.clone(),
                 cx,
             )
@@ -3939,6 +3897,7 @@ mod tests {
 
         clients.push(cx.foreground().spawn(host.simulate_host(
             host_project.clone(),
+            language_server_config,
             operations.clone(),
             max_operations,
             rng.clone(),
@@ -4268,113 +4227,160 @@ mod tests {
             )
         }
 
-        async fn simulate_host(
+        fn simulate_host(
             mut self,
             project: ModelHandle<Project>,
+            mut language_server_config: LanguageServerConfig,
             operations: Rc<Cell<usize>>,
             max_operations: usize,
             rng: Rc<RefCell<StdRng>>,
             mut cx: TestAppContext,
-        ) -> (Self, TestAppContext) {
-            let fs = project.read_with(&cx, |project, _| project.fs().clone());
-            let mut files: Vec<PathBuf> = Default::default();
-            while operations.get() < max_operations {
-                operations.set(operations.get() + 1);
+        ) -> impl Future<Output = (Self, TestAppContext)> {
+            // Set up a fake language server.
+            language_server_config.set_fake_initializer(|fake_server| {
+                fake_server.handle_request::<lsp::request::Completion, _>(|_| {
+                    Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem {
+                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+                            range: lsp::Range::new(
+                                lsp::Position::new(0, 0),
+                                lsp::Position::new(0, 0),
+                            ),
+                            new_text: "the-new-text".to_string(),
+                        })),
+                        ..Default::default()
+                    }]))
+                });
 
-                let distribution = rng.borrow_mut().gen_range(0..100);
-                match distribution {
-                    0..=20 if !files.is_empty() => {
-                        let mut path = files.choose(&mut *rng.borrow_mut()).unwrap().as_path();
-                        while let Some(parent_path) = path.parent() {
-                            path = parent_path;
-                            if rng.borrow_mut().gen() {
-                                break;
+                fake_server.handle_request::<lsp::request::CodeActionRequest, _>(|_| {
+                    Some(vec![lsp::CodeActionOrCommand::CodeAction(
+                        lsp::CodeAction {
+                            title: "the-code-action".to_string(),
+                            ..Default::default()
+                        },
+                    )])
+                });
+
+                fake_server.handle_request::<lsp::request::PrepareRenameRequest, _>(|params| {
+                    Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
+                        params.position,
+                        params.position,
+                    )))
+                });
+            });
+
+            project.update(&mut cx, |project, _| {
+                project.languages().add(Arc::new(Language::new(
+                    LanguageConfig {
+                        name: "Rust".into(),
+                        path_suffixes: vec!["rs".to_string()],
+                        language_server: Some(language_server_config),
+                        ..Default::default()
+                    },
+                    None,
+                )));
+            });
+
+            async move {
+                let fs = project.read_with(&cx, |project, _| project.fs().clone());
+                let mut files: Vec<PathBuf> = Default::default();
+                while operations.get() < max_operations {
+                    operations.set(operations.get() + 1);
+
+                    let distribution = rng.borrow_mut().gen_range(0..100);
+                    match distribution {
+                        0..=20 if !files.is_empty() => {
+                            let mut path = files.choose(&mut *rng.borrow_mut()).unwrap().as_path();
+                            while let Some(parent_path) = path.parent() {
+                                path = parent_path;
+                                if rng.borrow_mut().gen() {
+                                    break;
+                                }
                             }
-                        }
 
-                        log::info!("Host: find/create local worktree {:?}", path);
-                        project
-                            .update(&mut cx, |project, cx| {
-                                project.find_or_create_local_worktree(path, false, cx)
-                            })
-                            .await
-                            .unwrap();
-                    }
-                    10..=80 if !files.is_empty() => {
-                        let buffer = if self.buffers.is_empty() || rng.borrow_mut().gen() {
-                            let file = files.choose(&mut *rng.borrow_mut()).unwrap();
-                            let (worktree, path) = project
-                                .update(&mut cx, |project, cx| {
-                                    project.find_or_create_local_worktree(file, false, cx)
-                                })
-                                .await
-                                .unwrap();
-                            let project_path =
-                                worktree.read_with(&cx, |worktree, _| (worktree.id(), path));
-                            log::info!("Host: opening path {:?}", project_path);
-                            let buffer = project
+                            log::info!("Host: find/create local worktree {:?}", path);
+                            project
                                 .update(&mut cx, |project, cx| {
-                                    project.open_buffer(project_path, cx)
+                                    project.find_or_create_local_worktree(path, false, cx)
                                 })
                                 .await
                                 .unwrap();
-                            self.buffers.insert(buffer.clone());
-                            buffer
-                        } else {
-                            self.buffers
-                                .iter()
-                                .choose(&mut *rng.borrow_mut())
-                                .unwrap()
-                                .clone()
-                        };
-
-                        if rng.borrow_mut().gen_bool(0.1) {
-                            cx.update(|cx| {
-                                log::info!(
-                                    "Host: dropping buffer {:?}",
-                                    buffer.read(cx).file().unwrap().full_path(cx)
-                                );
-                                self.buffers.remove(&buffer);
-                                drop(buffer);
-                            });
-                        } else {
-                            buffer.update(&mut cx, |buffer, cx| {
-                                log::info!(
-                                    "Host: updating buffer {:?}",
-                                    buffer.file().unwrap().full_path(cx)
-                                );
-                                buffer.randomly_edit(&mut *rng.borrow_mut(), 5, cx)
-                            });
-                        }
-                    }
-                    _ => loop {
-                        let path_component_count = rng.borrow_mut().gen_range(1..=5);
-                        let mut path = PathBuf::new();
-                        path.push("/");
-                        for _ in 0..path_component_count {
-                            let letter = rng.borrow_mut().gen_range(b'a'..=b'z');
-                            path.push(std::str::from_utf8(&[letter]).unwrap());
                         }
-                        path.set_extension("rs");
-                        let parent_path = path.parent().unwrap();
-
-                        log::info!("Host: creating file {:?}", path);
-                        if fs.create_dir(&parent_path).await.is_ok()
-                            && fs.create_file(&path, Default::default()).await.is_ok()
-                        {
-                            files.push(path);
-                            break;
-                        } else {
-                            log::info!("Host: cannot create file");
+                        10..=80 if !files.is_empty() => {
+                            let buffer = if self.buffers.is_empty() || rng.borrow_mut().gen() {
+                                let file = files.choose(&mut *rng.borrow_mut()).unwrap();
+                                let (worktree, path) = project
+                                    .update(&mut cx, |project, cx| {
+                                        project.find_or_create_local_worktree(file, false, cx)
+                                    })
+                                    .await
+                                    .unwrap();
+                                let project_path =
+                                    worktree.read_with(&cx, |worktree, _| (worktree.id(), path));
+                                log::info!("Host: opening path {:?}", project_path);
+                                let buffer = project
+                                    .update(&mut cx, |project, cx| {
+                                        project.open_buffer(project_path, cx)
+                                    })
+                                    .await
+                                    .unwrap();
+                                self.buffers.insert(buffer.clone());
+                                buffer
+                            } else {
+                                self.buffers
+                                    .iter()
+                                    .choose(&mut *rng.borrow_mut())
+                                    .unwrap()
+                                    .clone()
+                            };
+
+                            if rng.borrow_mut().gen_bool(0.1) {
+                                cx.update(|cx| {
+                                    log::info!(
+                                        "Host: dropping buffer {:?}",
+                                        buffer.read(cx).file().unwrap().full_path(cx)
+                                    );
+                                    self.buffers.remove(&buffer);
+                                    drop(buffer);
+                                });
+                            } else {
+                                buffer.update(&mut cx, |buffer, cx| {
+                                    log::info!(
+                                        "Host: updating buffer {:?}",
+                                        buffer.file().unwrap().full_path(cx)
+                                    );
+                                    buffer.randomly_edit(&mut *rng.borrow_mut(), 5, cx)
+                                });
+                            }
                         }
-                    },
+                        _ => loop {
+                            let path_component_count = rng.borrow_mut().gen_range(1..=5);
+                            let mut path = PathBuf::new();
+                            path.push("/");
+                            for _ in 0..path_component_count {
+                                let letter = rng.borrow_mut().gen_range(b'a'..=b'z');
+                                path.push(std::str::from_utf8(&[letter]).unwrap());
+                            }
+                            path.set_extension("rs");
+                            let parent_path = path.parent().unwrap();
+
+                            log::info!("Host: creating file {:?}", path);
+                            if fs.create_dir(&parent_path).await.is_ok()
+                                && fs.create_file(&path, Default::default()).await.is_ok()
+                            {
+                                files.push(path);
+                                break;
+                            } else {
+                                log::info!("Host: cannot create file");
+                            }
+                        },
+                    }
+
+                    cx.background().simulate_random_delay().await;
                 }
 
-                cx.background().simulate_random_delay().await;
+                self.project = Some(project);
+                (self, cx)
             }
-
-            self.project = Some(project);
-            (self, cx)
         }
 
         pub async fn simulate_guest(

crates/workspace/src/lsp_status.rs 🔗

@@ -37,7 +37,7 @@ impl LspStatus {
                             &mut this.downloading,
                             &mut this.failed,
                         ] {
-                            vector.retain(|name| name != language.name());
+                            vector.retain(|name| name != language.name().as_ref());
                         }
 
                         match event {

crates/workspace/src/settings.rs 🔗

@@ -11,7 +11,7 @@ pub struct Settings {
     pub tab_size: usize,
     soft_wrap: SoftWrap,
     preferred_line_length: u32,
-    overrides: HashMap<String, Override>,
+    overrides: HashMap<Arc<str>, Override>,
     pub theme: Arc<Theme>,
 }
 
@@ -50,21 +50,25 @@ impl Settings {
         self
     }
 
-    pub fn with_overrides(mut self, language_name: impl Into<String>, overrides: Override) -> Self {
+    pub fn with_overrides(
+        mut self,
+        language_name: impl Into<Arc<str>>,
+        overrides: Override,
+    ) -> Self {
         self.overrides.insert(language_name.into(), overrides);
         self
     }
 
     pub fn soft_wrap(&self, language: Option<&Arc<Language>>) -> SoftWrap {
         language
-            .and_then(|language| self.overrides.get(language.name()))
+            .and_then(|language| self.overrides.get(language.name().as_ref()))
             .and_then(|settings| settings.soft_wrap)
             .unwrap_or(self.soft_wrap)
     }
 
     pub fn preferred_line_length(&self, language: Option<&Arc<Language>>) -> u32 {
         language
-            .and_then(|language| self.overrides.get(language.name()))
+            .and_then(|language| self.overrides.get(language.name().as_ref()))
             .and_then(|settings| settings.preferred_line_length)
             .unwrap_or(self.preferred_line_length)
     }

crates/zed/src/test.rs 🔗

@@ -25,18 +25,15 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
     let http = FakeHttpClient::with_404_response();
     let client = Client::new(http.clone());
     let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
-    let mut languages = LanguageRegistry::new();
-    languages.add(
-        Arc::new(language::Language::new(
-            language::LanguageConfig {
-                name: "Rust".to_string(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        )),
-        
-    );
+    let languages = LanguageRegistry::new();
+    languages.add(Arc::new(language::Language::new(
+        language::LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    )));
     Arc::new(AppState {
         settings_tx: Arc::new(Mutex::new(settings_tx)),
         settings,

crates/zed/src/zed.rs 🔗

@@ -534,7 +534,7 @@ mod tests {
         editor.read_with(&cx, |editor, cx| {
             assert!(!editor.is_dirty(cx));
             assert_eq!(editor.title(cx), "the-new-name.rs");
-            assert_eq!(editor.language(cx).unwrap().name(), "Rust");
+            assert_eq!(editor.language(cx).unwrap().name().as_ref(), "Rust");
         });
 
         // Edit the file and save it again. This time, there is no filename prompt.
@@ -614,7 +614,7 @@ mod tests {
         // The buffer is not dirty anymore and the language is assigned based on the path.
         editor.read_with(&cx, |editor, cx| {
             assert!(!editor.is_dirty(cx));
-            assert_eq!(editor.language(cx).unwrap().name(), "Rust")
+            assert_eq!(editor.language(cx).unwrap().name().as_ref(), "Rust")
         });
     }