From c595ed19d6908fd6a9310013d2cfd75613a8968f Mon Sep 17 00:00:00 2001 From: tidely <43219534+tidely@users.noreply.github.com> Date: Tue, 5 Aug 2025 23:46:57 +0300 Subject: [PATCH] languages: Remove a eager conversion from `LanguageName` to `String` (#35667) This PR changes the signature of `language_names` from ```rust pub fn language_names(&self) -> Vec // Into pub fn language_names(&self) -> Vec ``` 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 --- crates/debugger_ui/src/new_process_modal.rs | 6 ++---- .../src/extension_store_test.rs | 19 +++++++++++++++---- crates/language/src/language.rs | 18 +++++++++--------- crates/language/src/language_registry.rs | 8 ++++---- .../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(-) diff --git a/crates/debugger_ui/src/new_process_modal.rs b/crates/debugger_ui/src/new_process_modal.rs index 42f77ab056..2695941bc0 100644 --- a/crates/debugger_ui/src/new_process_modal.rs +++ b/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(), }) diff --git a/crates/extension_host/src/extension_store_test.rs b/crates/extension_host/src/extension_store_test.rs index 891ab91852..c31774c20d 100644 --- a/crates/extension_host/src/extension_store_test.rs +++ b/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(), []); }); } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 894625b982..b9933dfcec 100644 --- a/crates/language/src/language.rs +++ b/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"), ] ); diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 85123d2373..ea988e8098 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -547,15 +547,15 @@ impl LanguageRegistry { self.state.read().language_settings.clone() } - pub fn language_names(&self) -> Vec { + pub fn language_names(&self) -> Vec { 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::>(); - 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 } diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 4c03430553..7ef57085bb 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/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::>(); Self { diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index 601b4620c5..028bf9fb68 100644 --- a/crates/languages/src/json.rs +++ b/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); diff --git a/crates/snippets_ui/src/snippets_ui.rs b/crates/snippets_ui/src/snippets_ui.rs index 1cc16c5576..a8710d1672 100644 --- a/crates/snippets_ui/src/snippets_ui.rs +++ b/crates/snippets_ui/src/snippets_ui.rs @@ -149,13 +149,12 @@ impl ScopeSelectorDelegate { scope_selector: WeakEntity, language_registry: Arc, ) -> 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::>(); let mut existing_scopes = HashSet::new(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index ec62ed33fd..8c89a7d85a 100644 --- a/crates/zed/src/zed.rs +++ b/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();