language: Add `LanguageName::new_static` to reduce allocations (#44380)

tidely created

Implements a specialized constructor `LanguageName::new_static` for
`&'static str` which reduces allocations.

`LanguageName::new` always backs the underlying `SharedString` with an
owned `Arc<str>` even when a `&'static str` is passed. This makes us
allocate each time we create a new `LanguageName` no matter what.
Creating a specialized constructor for `&'static str` allows us to
essentially construct them for free.

Additional change:
Encourages using explicit constructors to avoid needless allocations.
Currently there were no instances of this trait being called where the
lifetime was not `'static` saving another 48 locations of allocation.

```rust
impl<'a> From<&'a str> for LanguageName {
    fn from(str: &'a str) -> Self {
        Self(SharedString::new(str))
    }
}

// to 

impl From<&'static str> for LanguageName {
    fn from(str: &'static str) -> Self {
        Self(SharedString::new_static(str))
    }
}

```

Release Notes:

- N/A

Change summary

crates/dap_adapters/src/python.rs                      |  2 
crates/editor/src/editor_tests.rs                      |  2 
crates/extension_host/src/extension_store_test.rs      | 14 ++--
crates/language/src/buffer_tests.rs                    |  6 
crates/language/src/language.rs                        | 20 +++---
crates/language/src/language_registry.rs               | 12 ++
crates/language_extension/src/extension_lsp_adapter.rs |  2 
crates/languages/src/json.rs                           |  4 
crates/languages/src/python.rs                         |  4 
crates/languages/src/tailwind.rs                       | 40 +++++++----
crates/languages/src/typescript.rs                     |  6 
crates/languages/src/vtsls.rs                          |  6 
crates/project/src/project_tests.rs                    |  4 
crates/project/src/terminals.rs                        |  4 
crates/repl/src/kernels/mod.rs                         |  2 
crates/vim/src/normal/paste.rs                         |  2 
16 files changed, 74 insertions(+), 56 deletions(-)

Detailed changes

crates/dap_adapters/src/python.rs 🔗

@@ -870,7 +870,7 @@ impl DebugAdapter for PythonDebugAdapter {
                 .active_toolchain(
                     delegate.worktree_id(),
                     base_path.into_arc(),
-                    language::LanguageName::new(Self::LANGUAGE_NAME),
+                    language::LanguageName::new_static(Self::LANGUAGE_NAME),
                     cx,
                 )
                 .await

crates/editor/src/editor_tests.rs 🔗

@@ -9970,7 +9970,7 @@ async fn test_autoindent_disabled_with_nested_language(cx: &mut TestAppContext)
                     ],
                     ..Default::default()
                 },
-                name: LanguageName::new("rust"),
+                name: LanguageName::new_static("rust"),
                 ..Default::default()
             },
             Some(tree_sitter_rust::LANGUAGE.into()),

crates/extension_host/src/extension_store_test.rs 🔗

@@ -307,9 +307,9 @@ async fn test_extension_store(cx: &mut TestAppContext) {
         assert_eq!(
             language_registry.language_names(),
             [
-                LanguageName::new("ERB"),
-                LanguageName::new("Plain Text"),
-                LanguageName::new("Ruby"),
+                LanguageName::new_static("ERB"),
+                LanguageName::new_static("Plain Text"),
+                LanguageName::new_static("Ruby"),
             ]
         );
         assert_eq!(
@@ -463,9 +463,9 @@ async fn test_extension_store(cx: &mut TestAppContext) {
         assert_eq!(
             language_registry.language_names(),
             [
-                LanguageName::new("ERB"),
-                LanguageName::new("Plain Text"),
-                LanguageName::new("Ruby"),
+                LanguageName::new_static("ERB"),
+                LanguageName::new_static("Plain Text"),
+                LanguageName::new_static("Ruby"),
             ]
         );
         assert_eq!(
@@ -523,7 +523,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
 
         assert_eq!(
             language_registry.language_names(),
-            [LanguageName::new("Plain Text")]
+            [LanguageName::new_static("Plain Text")]
         );
         assert_eq!(language_registry.grammar_names(), []);
     });

crates/language/src/buffer_tests.rs 🔗

@@ -151,7 +151,7 @@ fn test_select_language(cx: &mut App) {
     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
     registry.add(Arc::new(Language::new(
         LanguageConfig {
-            name: LanguageName::new("Rust"),
+            name: LanguageName::new_static("Rust"),
             matcher: LanguageMatcher {
                 path_suffixes: vec!["rs".to_string()],
                 ..Default::default()
@@ -173,7 +173,7 @@ fn test_select_language(cx: &mut App) {
     )));
     registry.add(Arc::new(Language::new(
         LanguageConfig {
-            name: LanguageName::new("Make"),
+            name: LanguageName::new_static("Make"),
             matcher: LanguageMatcher {
                 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
                 ..Default::default()
@@ -3728,7 +3728,7 @@ fn ruby_lang() -> Language {
 fn html_lang() -> Language {
     Language::new(
         LanguageConfig {
-            name: LanguageName::new("HTML"),
+            name: LanguageName::new_static("HTML"),
             block_comment: Some(BlockCommentConfig {
                 start: "<!--".into(),
                 prefix: "".into(),

crates/language/src/language.rs 🔗

@@ -982,7 +982,7 @@ impl<T> Override<T> {
 impl Default for LanguageConfig {
     fn default() -> Self {
         Self {
-            name: LanguageName::new(""),
+            name: LanguageName::new_static(""),
             code_fence_block_name: None,
             grammar: None,
             matcher: LanguageMatcher::default(),
@@ -2756,9 +2756,9 @@ mod tests {
         assert_eq!(
             languages.language_names(),
             &[
-                LanguageName::new("JSON"),
-                LanguageName::new("Plain Text"),
-                LanguageName::new("Rust"),
+                LanguageName::new_static("JSON"),
+                LanguageName::new_static("Plain Text"),
+                LanguageName::new_static("Rust"),
             ]
         );
 
@@ -2769,9 +2769,9 @@ mod tests {
         assert_eq!(
             languages.language_names(),
             &[
-                LanguageName::new("JSON"),
-                LanguageName::new("Plain Text"),
-                LanguageName::new("Rust"),
+                LanguageName::new_static("JSON"),
+                LanguageName::new_static("Plain Text"),
+                LanguageName::new_static("Rust"),
             ]
         );
 
@@ -2782,9 +2782,9 @@ mod tests {
         assert_eq!(
             languages.language_names(),
             &[
-                LanguageName::new("JSON"),
-                LanguageName::new("Plain Text"),
-                LanguageName::new("Rust"),
+                LanguageName::new_static("JSON"),
+                LanguageName::new_static("Plain Text"),
+                LanguageName::new_static("Rust"),
             ]
         );
 

crates/language/src/language_registry.rs 🔗

@@ -43,12 +43,18 @@ impl LanguageName {
         Self(SharedString::new(s))
     }
 
+    pub fn new_static(s: &'static str) -> Self {
+        Self(SharedString::new_static(s))
+    }
+
     pub fn from_proto(s: String) -> Self {
         Self(SharedString::from(s))
     }
+
     pub fn to_proto(&self) -> String {
         self.0.to_string()
     }
+
     pub fn lsp_id(&self) -> String {
         match self.0.as_ref() {
             "Plain Text" => "plaintext".to_string(),
@@ -87,9 +93,9 @@ impl std::fmt::Display for LanguageName {
     }
 }
 
-impl<'a> From<&'a str> for LanguageName {
-    fn from(str: &'a str) -> LanguageName {
-        LanguageName(SharedString::new(str))
+impl From<&'static str> for LanguageName {
+    fn from(str: &'static str) -> Self {
+        Self(SharedString::new_static(str))
     }
 }
 

crates/language_extension/src/extension_lsp_adapter.rs 🔗

@@ -245,7 +245,7 @@ impl LspAdapter for ExtensionLspAdapter {
         // We can remove once the following extension versions no longer see any use:
         // - php@0.0.1
         if self.extension.manifest().id.as_ref() == "php" {
-            return HashMap::from_iter([(LanguageName::new("PHP"), "php".into())]);
+            return HashMap::from_iter([(LanguageName::new_static("PHP"), "php".into())]);
         }
 
         self.extension

crates/languages/src/json.rs 🔗

@@ -286,8 +286,8 @@ impl LspAdapter for JsonLspAdapter {
 
     fn language_ids(&self) -> HashMap<LanguageName, String> {
         [
-            (LanguageName::new("JSON"), "json".into()),
-            (LanguageName::new("JSONC"), "jsonc".into()),
+            (LanguageName::new_static("JSON"), "json".into()),
+            (LanguageName::new_static("JSONC"), "jsonc".into()),
         ]
         .into_iter()
         .collect()

crates/languages/src/python.rs 🔗

@@ -903,7 +903,7 @@ impl ContextProvider for PythonContextProvider {
 
 fn selected_test_runner(location: Option<&Arc<dyn language::File>>, cx: &App) -> TestRunner {
     const TEST_RUNNER_VARIABLE: &str = "TEST_RUNNER";
-    language_settings(Some(LanguageName::new("Python")), location, cx)
+    language_settings(Some(LanguageName::new_static("Python")), location, cx)
         .tasks
         .variables
         .get(TEST_RUNNER_VARIABLE)
@@ -1397,7 +1397,7 @@ async fn venv_to_toolchain(venv: PythonEnvironment, fs: &dyn Fs) -> Option<Toolc
             .to_str()?
             .to_owned()
             .into(),
-        language_name: LanguageName::new("Python"),
+        language_name: LanguageName::new_static("Python"),
         as_json: serde_json::to_value(data).ok()?,
     })
 }

crates/languages/src/tailwind.rs 🔗

@@ -174,20 +174,32 @@ impl LspAdapter for TailwindLspAdapter {
 
     fn language_ids(&self) -> HashMap<LanguageName, String> {
         HashMap::from_iter([
-            (LanguageName::new("Astro"), "astro".to_string()),
-            (LanguageName::new("HTML"), "html".to_string()),
-            (LanguageName::new("Gleam"), "html".to_string()),
-            (LanguageName::new("CSS"), "css".to_string()),
-            (LanguageName::new("JavaScript"), "javascript".to_string()),
-            (LanguageName::new("TypeScript"), "typescript".to_string()),
-            (LanguageName::new("TSX"), "typescriptreact".to_string()),
-            (LanguageName::new("Svelte"), "svelte".to_string()),
-            (LanguageName::new("Elixir"), "phoenix-heex".to_string()),
-            (LanguageName::new("HEEX"), "phoenix-heex".to_string()),
-            (LanguageName::new("ERB"), "erb".to_string()),
-            (LanguageName::new("HTML+ERB"), "erb".to_string()),
-            (LanguageName::new("PHP"), "php".to_string()),
-            (LanguageName::new("Vue.js"), "vue".to_string()),
+            (LanguageName::new_static("Astro"), "astro".to_string()),
+            (LanguageName::new_static("HTML"), "html".to_string()),
+            (LanguageName::new_static("Gleam"), "html".to_string()),
+            (LanguageName::new_static("CSS"), "css".to_string()),
+            (
+                LanguageName::new_static("JavaScript"),
+                "javascript".to_string(),
+            ),
+            (
+                LanguageName::new_static("TypeScript"),
+                "typescript".to_string(),
+            ),
+            (
+                LanguageName::new_static("TSX"),
+                "typescriptreact".to_string(),
+            ),
+            (LanguageName::new_static("Svelte"), "svelte".to_string()),
+            (
+                LanguageName::new_static("Elixir"),
+                "phoenix-heex".to_string(),
+            ),
+            (LanguageName::new_static("HEEX"), "phoenix-heex".to_string()),
+            (LanguageName::new_static("ERB"), "erb".to_string()),
+            (LanguageName::new_static("HTML+ERB"), "erb".to_string()),
+            (LanguageName::new_static("PHP"), "php".to_string()),
+            (LanguageName::new_static("Vue.js"), "vue".to_string()),
         ])
     }
 }

crates/languages/src/typescript.rs 🔗

@@ -822,9 +822,9 @@ impl LspAdapter for TypeScriptLspAdapter {
 
     fn language_ids(&self) -> HashMap<LanguageName, String> {
         HashMap::from_iter([
-            (LanguageName::new("TypeScript"), "typescript".into()),
-            (LanguageName::new("JavaScript"), "javascript".into()),
-            (LanguageName::new("TSX"), "typescriptreact".into()),
+            (LanguageName::new_static("TypeScript"), "typescript".into()),
+            (LanguageName::new_static("JavaScript"), "javascript".into()),
+            (LanguageName::new_static("TSX"), "typescriptreact".into()),
         ])
     }
 }

crates/languages/src/vtsls.rs 🔗

@@ -296,9 +296,9 @@ impl LspAdapter for VtslsLspAdapter {
 
     fn language_ids(&self) -> HashMap<LanguageName, String> {
         HashMap::from_iter([
-            (LanguageName::new("TypeScript"), "typescript".into()),
-            (LanguageName::new("JavaScript"), "javascript".into()),
-            (LanguageName::new("TSX"), "typescriptreact".into()),
+            (LanguageName::new_static("TypeScript"), "typescript".into()),
+            (LanguageName::new_static("JavaScript"), "javascript".into()),
+            (LanguageName::new_static("TSX"), "typescriptreact".into()),
         ])
     }
 }

crates/project/src/project_tests.rs 🔗

@@ -746,7 +746,7 @@ async fn test_running_multiple_instances_of_a_single_server_in_one_worktree(
                     worktree_id,
                     path: rel_path("project-b/source_file.py").into(),
                 },
-                LanguageName::new("Python"),
+                LanguageName::new_static("Python"),
                 cx,
             )
         })
@@ -762,7 +762,7 @@ async fn test_running_multiple_instances_of_a_single_server_in_one_worktree(
                     worktree_id,
                     path: rel_path("project-b/source_file.py").into(),
                 },
-                LanguageName::new("Python"),
+                LanguageName::new_static("Python"),
                 cx,
             )
         })

crates/project/src/terminals.rs 🔗

@@ -111,7 +111,7 @@ impl Project {
             );
         let toolchains = project_path_contexts
             .filter(|_| detect_venv)
-            .map(|p| self.active_toolchain(p, LanguageName::new("Python"), cx))
+            .map(|p| self.active_toolchain(p, LanguageName::new_static("Python"), cx))
             .collect::<Vec<_>>();
         let lang_registry = self.languages.clone();
         cx.spawn(async move |project, cx| {
@@ -311,7 +311,7 @@ impl Project {
             );
         let toolchains = project_path_contexts
             .filter(|_| detect_venv)
-            .map(|p| self.active_toolchain(p, LanguageName::new("Python"), cx))
+            .map(|p| self.active_toolchain(p, LanguageName::new_static("Python"), cx))
             .collect::<Vec<_>>();
         let remote_client = self.remote_client.clone();
         let shell = match &remote_client {

crates/repl/src/kernels/mod.rs 🔗

@@ -81,7 +81,7 @@ pub fn python_env_kernel_specifications(
     worktree_id: WorktreeId,
     cx: &mut App,
 ) -> impl Future<Output = Result<Vec<KernelSpecification>>> + use<> {
-    let python_language = LanguageName::new("Python");
+    let python_language = LanguageName::new_static("Python");
     let toolchains = project.read(cx).available_toolchains(
         ProjectPath {
             worktree_id,

crates/vim/src/normal/paste.rs 🔗

@@ -717,7 +717,7 @@ mod test {
         cx.update_global(|store: &mut SettingsStore, cx| {
             store.update_user_settings(cx, |settings| {
                 settings.project.all_languages.languages.0.insert(
-                    LanguageName::new("Rust").0,
+                    LanguageName::new_static("Rust").0,
                     LanguageSettingsContent {
                         auto_indent_on_paste: Some(false),
                         ..Default::default()