Use a better type for language IDs field (#35566)

Kirill Bulatov created

Part of the preparation for proto capabilities.

Release Notes:

- N/A

Change summary

crates/extension/src/extension_manifest.rs             |  2 
crates/language/src/language.rs                        |  9 ++-
crates/language_extension/src/extension_lsp_adapter.rs |  4 
crates/languages/src/json.rs                           | 10 ++--
crates/languages/src/tailwind.rs                       | 28 ++++++------
crates/languages/src/typescript.rs                     | 11 ++--
crates/languages/src/vtsls.rs                          | 10 ++--
crates/project/src/lsp_command.rs                      | 23 +++++----
crates/project/src/lsp_store.rs                        |  5 -
9 files changed, 52 insertions(+), 50 deletions(-)

Detailed changes

crates/extension/src/extension_manifest.rs 🔗

@@ -163,7 +163,7 @@ pub struct LanguageServerManifestEntry {
     #[serde(default)]
     languages: Vec<LanguageName>,
     #[serde(default)]
-    pub language_ids: HashMap<String, String>,
+    pub language_ids: HashMap<LanguageName, String>,
     #[serde(default)]
     pub code_action_kinds: Option<Vec<lsp::CodeActionKind>>,
 }

crates/language/src/language.rs 🔗

@@ -161,7 +161,7 @@ pub struct CachedLspAdapter {
     pub name: LanguageServerName,
     pub disk_based_diagnostic_sources: Vec<String>,
     pub disk_based_diagnostics_progress_token: Option<String>,
-    language_ids: HashMap<String, String>,
+    language_ids: HashMap<LanguageName, String>,
     pub adapter: Arc<dyn LspAdapter>,
     pub reinstall_attempt_count: AtomicU64,
     cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,
@@ -277,10 +277,11 @@ impl CachedLspAdapter {
 
     pub fn language_id(&self, language_name: &LanguageName) -> String {
         self.language_ids
-            .get(language_name.as_ref())
+            .get(language_name)
             .cloned()
             .unwrap_or_else(|| language_name.lsp_id())
     }
+
     pub fn manifest_name(&self) -> Option<ManifestName> {
         self.manifest_name
             .get_or_init(|| self.adapter.manifest_name())
@@ -573,8 +574,8 @@ pub trait LspAdapter: 'static + Send + Sync {
         None
     }
 
-    fn language_ids(&self) -> HashMap<String, String> {
-        Default::default()
+    fn language_ids(&self) -> HashMap<LanguageName, String> {
+        HashMap::default()
     }
 
     /// Support custom initialize params.

crates/language_extension/src/extension_lsp_adapter.rs 🔗

@@ -242,7 +242,7 @@ impl LspAdapter for ExtensionLspAdapter {
         ]))
     }
 
-    fn language_ids(&self) -> HashMap<String, String> {
+    fn language_ids(&self) -> HashMap<LanguageName, String> {
         // TODO: The language IDs can be provided via the language server options
         // in `extension.toml now but we're leaving these existing usages in place temporarily
         // to avoid any compatibility issues between Zed and the extension versions.
@@ -250,7 +250,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([("PHP".into(), "php".into())]);
+            return HashMap::from_iter([(LanguageName::new("PHP"), "php".into())]);
         }
 
         self.extension

crates/languages/src/json.rs 🔗

@@ -8,8 +8,8 @@ use futures::StreamExt;
 use gpui::{App, AsyncApp, Task};
 use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
 use language::{
-    ContextProvider, LanguageRegistry, LanguageToolchainStore, LocalFile as _, LspAdapter,
-    LspAdapterDelegate,
+    ContextProvider, LanguageName, LanguageRegistry, LanguageToolchainStore, LocalFile as _,
+    LspAdapter, LspAdapterDelegate,
 };
 use lsp::{LanguageServerBinary, LanguageServerName};
 use node_runtime::NodeRuntime;
@@ -408,10 +408,10 @@ impl LspAdapter for JsonLspAdapter {
         Ok(config)
     }
 
-    fn language_ids(&self) -> HashMap<String, String> {
+    fn language_ids(&self) -> HashMap<LanguageName, String> {
         [
-            ("JSON".into(), "json".into()),
-            ("JSONC".into(), "jsonc".into()),
+            (LanguageName::new("JSON"), "json".into()),
+            (LanguageName::new("JSONC"), "jsonc".into()),
         ]
         .into_iter()
         .collect()

crates/languages/src/tailwind.rs 🔗

@@ -3,7 +3,7 @@ use async_trait::async_trait;
 use collections::HashMap;
 use futures::StreamExt;
 use gpui::AsyncApp;
-use language::{LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
+use language::{LanguageName, LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
 use lsp::{LanguageServerBinary, LanguageServerName};
 use node_runtime::NodeRuntime;
 use project::{Fs, lsp_store::language_server_settings};
@@ -168,20 +168,20 @@ impl LspAdapter for TailwindLspAdapter {
         }))
     }
 
-    fn language_ids(&self) -> HashMap<String, String> {
+    fn language_ids(&self) -> HashMap<LanguageName, String> {
         HashMap::from_iter([
-            ("Astro".to_string(), "astro".to_string()),
-            ("HTML".to_string(), "html".to_string()),
-            ("CSS".to_string(), "css".to_string()),
-            ("JavaScript".to_string(), "javascript".to_string()),
-            ("TSX".to_string(), "typescriptreact".to_string()),
-            ("Svelte".to_string(), "svelte".to_string()),
-            ("Elixir".to_string(), "phoenix-heex".to_string()),
-            ("HEEX".to_string(), "phoenix-heex".to_string()),
-            ("ERB".to_string(), "erb".to_string()),
-            ("HTML/ERB".to_string(), "erb".to_string()),
-            ("PHP".to_string(), "php".to_string()),
-            ("Vue.js".to_string(), "vue".to_string()),
+            (LanguageName::new("Astro"), "astro".to_string()),
+            (LanguageName::new("HTML"), "html".to_string()),
+            (LanguageName::new("CSS"), "css".to_string()),
+            (LanguageName::new("JavaScript"), "javascript".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()),
         ])
     }
 }

crates/languages/src/typescript.rs 🔗

@@ -8,7 +8,8 @@ use futures::future::join_all;
 use gpui::{App, AppContext, AsyncApp, Task};
 use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url};
 use language::{
-    ContextLocation, ContextProvider, File, LanguageToolchainStore, LspAdapter, LspAdapterDelegate,
+    ContextLocation, ContextProvider, File, LanguageName, LanguageToolchainStore, LspAdapter,
+    LspAdapterDelegate,
 };
 use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
 use node_runtime::NodeRuntime;
@@ -741,11 +742,11 @@ impl LspAdapter for TypeScriptLspAdapter {
         }))
     }
 
-    fn language_ids(&self) -> HashMap<String, String> {
+    fn language_ids(&self) -> HashMap<LanguageName, String> {
         HashMap::from_iter([
-            ("TypeScript".into(), "typescript".into()),
-            ("JavaScript".into(), "javascript".into()),
-            ("TSX".into(), "typescriptreact".into()),
+            (LanguageName::new("TypeScript"), "typescript".into()),
+            (LanguageName::new("JavaScript"), "javascript".into()),
+            (LanguageName::new("TSX"), "typescriptreact".into()),
         ])
     }
 }

crates/languages/src/vtsls.rs 🔗

@@ -2,7 +2,7 @@ use anyhow::Result;
 use async_trait::async_trait;
 use collections::HashMap;
 use gpui::AsyncApp;
-use language::{LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
+use language::{LanguageName, LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
 use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
 use node_runtime::NodeRuntime;
 use project::{Fs, lsp_store::language_server_settings};
@@ -273,11 +273,11 @@ impl LspAdapter for VtslsLspAdapter {
         Ok(default_workspace_configuration)
     }
 
-    fn language_ids(&self) -> HashMap<String, String> {
+    fn language_ids(&self) -> HashMap<LanguageName, String> {
         HashMap::from_iter([
-            ("TypeScript".into(), "typescript".into()),
-            ("JavaScript".into(), "javascript".into()),
-            ("TSX".into(), "typescriptreact".into()),
+            (LanguageName::new("TypeScript"), "typescript".into()),
+            (LanguageName::new("JavaScript"), "javascript".into()),
+            (LanguageName::new("TSX"), "typescriptreact".into()),
         ])
     }
 }

crates/project/src/lsp_command.rs 🔗

@@ -3580,6 +3580,18 @@ impl LspCommand for GetCodeLens {
     }
 }
 
+impl LinkedEditingRange {
+    pub fn check_server_capabilities(capabilities: ServerCapabilities) -> bool {
+        let Some(linked_editing_options) = capabilities.linked_editing_range_provider else {
+            return false;
+        };
+        if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
+            return false;
+        }
+        true
+    }
+}
+
 #[async_trait(?Send)]
 impl LspCommand for LinkedEditingRange {
     type Response = Vec<Range<Anchor>>;
@@ -3591,16 +3603,7 @@ impl LspCommand for LinkedEditingRange {
     }
 
     fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
-        let Some(linked_editing_options) = &capabilities
-            .server_capabilities
-            .linked_editing_range_provider
-        else {
-            return false;
-        };
-        if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
-            return false;
-        }
-        true
+        Self::check_server_capabilities(capabilities.server_capabilities)
     }
 
     fn to_lsp(

crates/project/src/lsp_store.rs 🔗

@@ -5069,10 +5069,7 @@ impl LspStore {
                     local
                         .language_servers_for_buffer(buffer, cx)
                         .filter(|(_, server)| {
-                            server
-                                .capabilities()
-                                .linked_editing_range_provider
-                                .is_some()
+                            LinkedEditingRange::check_server_capabilities(server.capabilities())
                         })
                         .filter(|(adapter, _)| {
                             scope