languages: Remove a eager conversion from `LanguageName` to `String` (#35667)

tidely created

This PR changes the signature of `language_names` from

```rust
pub fn language_names(&self) -> Vec<String>
// Into
pub fn language_names(&self) -> Vec<LanguageName>
```

The function previously eagerly converted `LanguageName`'s to
`String`'s, which requires the reallocation of all of the elements. The
functions get called in many places in the code base, but only one of
which actually requires the conversion to a `String`. In one case it
would do a `SharedString` -> `String` -> `SharedString` conversion,
which is now totally bypassed.

Release Notes:

- N/A

Change summary

crates/debugger_ui/src/new_process_modal.rs       |  6 +---
crates/extension_host/src/extension_store_test.rs | 19 +++++++++++++---
crates/language/src/language.rs                   | 18 ++++++++--------
crates/language/src/language_registry.rs          |  8 +++---
crates/language_selector/src/language_selector.rs |  4 +-
crates/languages/src/json.rs                      | 10 ++++++++
crates/snippets_ui/src/snippets_ui.rs             |  5 +--
crates/zed/src/zed.rs                             |  2 
8 files changed, 44 insertions(+), 28 deletions(-)

Detailed changes

crates/debugger_ui/src/new_process_modal.rs 🔗

@@ -1015,15 +1015,13 @@ impl DebugDelegate {
         let language_names = languages.language_names();
         let language = dap_registry
             .adapter_language(&scenario.adapter)
-            .map(|language| TaskSourceKind::Language {
-                name: language.into(),
-            });
+            .map(|language| TaskSourceKind::Language { name: language.0 });
 
         let language = language.or_else(|| {
             scenario.label.split_whitespace().find_map(|word| {
                 language_names
                     .iter()
-                    .find(|name| name.eq_ignore_ascii_case(word))
+                    .find(|name| name.as_ref().eq_ignore_ascii_case(word))
                     .map(|name| TaskSourceKind::Language {
                         name: name.to_owned().into(),
                     })

crates/extension_host/src/extension_store_test.rs 🔗

@@ -10,7 +10,7 @@ use fs::{FakeFs, Fs, RealFs};
 use futures::{AsyncReadExt, StreamExt, io::BufReader};
 use gpui::{AppContext as _, SemanticVersion, TestAppContext};
 use http_client::{FakeHttpClient, Response};
-use language::{BinaryStatus, LanguageMatcher, LanguageRegistry};
+use language::{BinaryStatus, LanguageMatcher, LanguageName, LanguageRegistry};
 use language_extension::LspAccess;
 use lsp::LanguageServerName;
 use node_runtime::NodeRuntime;
@@ -306,7 +306,11 @@ async fn test_extension_store(cx: &mut TestAppContext) {
 
         assert_eq!(
             language_registry.language_names(),
-            ["ERB", "Plain Text", "Ruby"]
+            [
+                LanguageName::new("ERB"),
+                LanguageName::new("Plain Text"),
+                LanguageName::new("Ruby"),
+            ]
         );
         assert_eq!(
             theme_registry.list_names(),
@@ -458,7 +462,11 @@ async fn test_extension_store(cx: &mut TestAppContext) {
 
         assert_eq!(
             language_registry.language_names(),
-            ["ERB", "Plain Text", "Ruby"]
+            [
+                LanguageName::new("ERB"),
+                LanguageName::new("Plain Text"),
+                LanguageName::new("Ruby"),
+            ]
         );
         assert_eq!(
             language_registry.grammar_names(),
@@ -513,7 +521,10 @@ async fn test_extension_store(cx: &mut TestAppContext) {
             assert_eq!(actual_language.hidden, expected_language.hidden);
         }
 
-        assert_eq!(language_registry.language_names(), ["Plain Text"]);
+        assert_eq!(
+            language_registry.language_names(),
+            [LanguageName::new("Plain Text")]
+        );
         assert_eq!(language_registry.grammar_names(), []);
     });
 }

crates/language/src/language.rs 🔗

@@ -2353,9 +2353,9 @@ mod tests {
         assert_eq!(
             languages.language_names(),
             &[
-                "JSON".to_string(),
-                "Plain Text".to_string(),
-                "Rust".to_string(),
+                LanguageName::new("JSON"),
+                LanguageName::new("Plain Text"),
+                LanguageName::new("Rust"),
             ]
         );
 
@@ -2366,9 +2366,9 @@ mod tests {
         assert_eq!(
             languages.language_names(),
             &[
-                "JSON".to_string(),
-                "Plain Text".to_string(),
-                "Rust".to_string(),
+                LanguageName::new("JSON"),
+                LanguageName::new("Plain Text"),
+                LanguageName::new("Rust"),
             ]
         );
 
@@ -2379,9 +2379,9 @@ mod tests {
         assert_eq!(
             languages.language_names(),
             &[
-                "JSON".to_string(),
-                "Plain Text".to_string(),
-                "Rust".to_string(),
+                LanguageName::new("JSON"),
+                LanguageName::new("Plain Text"),
+                LanguageName::new("Rust"),
             ]
         );
 

crates/language/src/language_registry.rs 🔗

@@ -547,15 +547,15 @@ impl LanguageRegistry {
         self.state.read().language_settings.clone()
     }
 
-    pub fn language_names(&self) -> Vec<String> {
+    pub fn language_names(&self) -> Vec<LanguageName> {
         let state = self.state.read();
         let mut result = state
             .available_languages
             .iter()
-            .filter_map(|l| l.loaded.not().then_some(l.name.to_string()))
-            .chain(state.languages.iter().map(|l| l.config.name.to_string()))
+            .filter_map(|l| l.loaded.not().then_some(l.name.clone()))
+            .chain(state.languages.iter().map(|l| l.config.name.clone()))
             .collect::<Vec<_>>();
-        result.sort_unstable_by_key(|language_name| language_name.to_lowercase());
+        result.sort_unstable_by_key(|language_name| language_name.as_ref().to_lowercase());
         result
     }
 

crates/language_selector/src/language_selector.rs 🔗

@@ -121,13 +121,13 @@ impl LanguageSelectorDelegate {
             .into_iter()
             .filter_map(|name| {
                 language_registry
-                    .available_language_for_name(&name)?
+                    .available_language_for_name(name.as_ref())?
                     .hidden()
                     .not()
                     .then_some(name)
             })
             .enumerate()
-            .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, &name))
+            .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name.as_ref()))
             .collect::<Vec<_>>();
 
         Self {

crates/languages/src/json.rs 🔗

@@ -269,7 +269,15 @@ impl JsonLspAdapter {
             .await;
 
         let config = cx.update(|cx| {
-            Self::get_workspace_config(self.languages.language_names().clone(), adapter_schemas, cx)
+            Self::get_workspace_config(
+                self.languages
+                    .language_names()
+                    .into_iter()
+                    .map(|name| name.to_string())
+                    .collect(),
+                adapter_schemas,
+                cx,
+            )
         })?;
         writer.replace(config.clone());
         return Ok(config);

crates/snippets_ui/src/snippets_ui.rs 🔗

@@ -149,13 +149,12 @@ impl ScopeSelectorDelegate {
         scope_selector: WeakEntity<ScopeSelector>,
         language_registry: Arc<LanguageRegistry>,
     ) -> Self {
-        let candidates = Vec::from([GLOBAL_SCOPE_NAME.to_string()]).into_iter();
         let languages = language_registry.language_names().into_iter();
 
-        let candidates = candidates
+        let candidates = std::iter::once(LanguageName::new(GLOBAL_SCOPE_NAME))
             .chain(languages)
             .enumerate()
-            .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, &name))
+            .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name.as_ref()))
             .collect::<Vec<_>>();
 
         let mut existing_scopes = HashSet::new();

crates/zed/src/zed.rs 🔗

@@ -4439,7 +4439,7 @@ mod tests {
         });
         for name in languages.language_names() {
             languages
-                .language_for_name(&name)
+                .language_for_name(name.as_ref())
                 .await
                 .with_context(|| format!("language name {name}"))
                 .unwrap();