diff --git a/crates/edit_prediction_context/src/fake_definition_lsp.rs b/crates/edit_prediction_context/src/fake_definition_lsp.rs index 1eb05291d4e..b950bc18ea2 100644 --- a/crates/edit_prediction_context/src/fake_definition_lsp.rs +++ b/crates/edit_prediction_context/src/fake_definition_lsp.rs @@ -34,7 +34,7 @@ pub fn register_fake_definition_server( arguments: Vec::new(), env: None, }, - enabled_by_default: true, + opt_in_languages: Default::default(), capabilities: lsp::ServerCapabilities { definition_provider: Some(lsp::OneOf::Left(true)), type_definition_provider: Some(lsp::TypeDefinitionProviderCapability::Simple(true)), diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index c7654cc64be..3e6143efa5f 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -334,7 +334,6 @@ pub struct CachedLspAdapter { pub disk_based_diagnostic_sources: Vec, pub disk_based_diagnostics_progress_token: Option, language_ids: HashMap, - pub enabled_by_default: bool, pub adapter: Arc, cached_binary: Arc, } @@ -352,7 +351,6 @@ impl Debug for CachedLspAdapter { &self.disk_based_diagnostics_progress_token, ) .field("language_ids", &self.language_ids) - .field("enabled_by_default", &self.enabled_by_default) .finish_non_exhaustive() } } @@ -363,14 +361,12 @@ impl CachedLspAdapter { let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources(); let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token(); let language_ids = adapter.language_ids(); - let enabled_by_default = adapter.enabled_by_default(); Arc::new(CachedLspAdapter { name, disk_based_diagnostic_sources, disk_based_diagnostics_progress_token, language_ids, - enabled_by_default, adapter, cached_binary: Default::default(), }) @@ -380,6 +376,12 @@ impl CachedLspAdapter { self.adapter.name() } + /// Returns whether the language server only starts for the given language + /// when it is explicitly listed in the `language_servers` setting. + pub fn is_opt_in_for(&self, language: &LanguageName) -> bool { + self.adapter.is_opt_in_for(language) + } + pub async fn get_language_server_command( self: Arc, delegate: Arc, @@ -660,10 +662,11 @@ pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller { HashMap::default() } - /// Whether the `...` wildcard in the `language_servers` setting includes this - /// language server. If `false`, it only starts when listed explicitly. - fn enabled_by_default(&self) -> bool { - true + /// Whether the `...` wildcard in the `language_servers` setting excludes this + /// language server for the given language, so that it only starts when + /// listed explicitly. + fn is_opt_in_for(&self, _language: &LanguageName) -> bool { + false } /// Support custom initialize params. @@ -933,7 +936,7 @@ pub struct FakeLspAdapter { pub disk_based_diagnostics_progress_token: Option, pub disk_based_diagnostics_sources: Vec, pub language_server_binary: LanguageServerBinary, - pub enabled_by_default: bool, + pub opt_in_languages: HashSet, pub capabilities: lsp::ServerCapabilities, pub initializer: Option>, @@ -1561,7 +1564,7 @@ impl Default for FakeLspAdapter { arguments: vec![], env: Default::default(), }, - enabled_by_default: true, + opt_in_languages: HashSet::default(), label_for_completion: None, } } @@ -1664,8 +1667,8 @@ impl LspAdapter for FakeLspAdapter { label_for_completion(item, language) } - fn enabled_by_default(&self) -> bool { - self.enabled_by_default + fn is_opt_in_for(&self, language: &LanguageName) -> bool { + self.opt_in_languages.contains(language) } fn is_extension(&self) -> bool { diff --git a/crates/language_extension/src/extension_lsp_adapter.rs b/crates/language_extension/src/extension_lsp_adapter.rs index 4bdaa9f5bb1..33f6201a6db 100644 --- a/crates/language_extension/src/extension_lsp_adapter.rs +++ b/crates/language_extension/src/extension_lsp_adapter.rs @@ -305,12 +305,12 @@ impl LspAdapter for ExtensionLspAdapter { .unwrap_or_default() } - fn enabled_by_default(&self) -> bool { + fn is_opt_in_for(&self, language: &LanguageName) -> bool { self.extension .manifest() .language_servers .get(&self.language_server_id) - .is_none_or(|server| !server.is_opt_in_for(&self.language_name)) + .is_some_and(|server| server.is_opt_in_for(language)) } async fn initialization_options( diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 00d37488527..3c7b3c6de5d 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -372,6 +372,10 @@ impl LspAdapter for TyLspAdapter { Self::SERVER_NAME } + fn is_opt_in_for(&self, language: &LanguageName) -> bool { + language == "Python" + } + async fn label_for_completion( &self, item: &lsp::CompletionItem, @@ -665,6 +669,10 @@ impl LspAdapter for PyrightLspAdapter { Self::SERVER_NAME } + fn is_opt_in_for(&self, language: &LanguageName) -> bool { + language == "Python" + } + async fn initialization_options( self: Arc, _: &Arc, @@ -1816,6 +1824,10 @@ impl LspAdapter for PyLspAdapter { Self::SERVER_NAME } + fn is_opt_in_for(&self, language: &LanguageName) -> bool { + language == "Python" + } + async fn process_completions(&self, items: &mut [lsp::CompletionItem]) { for item in items { let is_named_argument = item.label.ends_with('='); diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index 806fa372d46..7245671c337 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -875,6 +875,10 @@ impl LspAdapter for TypeScriptLspAdapter { (LanguageName::new_static("TSX"), "typescriptreact".into()), ]) } + + fn is_opt_in_for(&self, language: &LanguageName) -> bool { + matches!(language.as_ref(), "TypeScript" | "JavaScript" | "TSX") + } } async fn get_cached_ts_server_binary( diff --git a/crates/project/src/manifest_tree/server_tree.rs b/crates/project/src/manifest_tree/server_tree.rs index a5499f48947..f38ff84cf67 100644 --- a/crates/project/src/manifest_tree/server_tree.rs +++ b/crates/project/src/manifest_tree/server_tree.rs @@ -253,7 +253,7 @@ impl LanguageServerTree { let available_lsp_adapters = self.languages.lsp_adapters(language_name); let available_language_servers = available_lsp_adapters .iter() - .filter(|lsp_adapter| lsp_adapter.enabled_by_default) + .filter(|lsp_adapter| !lsp_adapter.is_opt_in_for(language_name)) .map(|lsp_adapter| lsp_adapter.name.clone()) .collect::>(); diff --git a/crates/project/tests/integration/project_tests.rs b/crates/project/tests/integration/project_tests.rs index 4928ca8c1cf..75d2b5ac904 100644 --- a/crates/project/tests/integration/project_tests.rs +++ b/crates/project/tests/integration/project_tests.rs @@ -2089,7 +2089,7 @@ async fn test_language_servers_disabled_by_default(cx: &mut gpui::TestAppContext "Rust", FakeLspAdapter { name: "opt-in-server", - enabled_by_default: false, + opt_in_languages: HashSet::from_iter([LanguageName::new_static("Rust")]), ..Default::default() }, ); diff --git a/docs/src/configuring-languages.md b/docs/src/configuring-languages.md index a798f073089..1d9897dadda 100644 --- a/docs/src/configuring-languages.md +++ b/docs/src/configuring-languages.md @@ -128,7 +128,7 @@ In this example: The `"..."` entry acts as a wildcard that includes any registered language server you haven't explicitly mentioned. Servers you list by name keep their position, and `"..."` fills in the remaining ones at that point in the list. Servers prefixed with `!` are excluded entirely. This means that if a new language server extension is installed or a new server is registered for a language, `"..."` will automatically include it. If you want full control over which servers are enabled, omit `"..."` — only the servers you list by name will be used. -Extensions can also mark a language server as [disabled by default](./extensions/languages.md#opt-in-language-servers) for some of its languages. Such servers are never included by `"..."`, regardless of your configuration, and only start when you list them by name, e.g. `["my-alternative-server", "..."]`. +Some language servers are disabled by default for some of their languages, e.g. `typescript-language-server` for TypeScript, or `ty` for Python. Extensions can also mark their language servers as [disabled by default](./extensions/languages.md#opt-in-language-servers). Such servers are never included by `"..."`, regardless of your configuration, and only start when you list them by name, e.g. `["my-alternative-server", "..."]`. #### Examples diff --git a/docs/src/languages/python.md b/docs/src/languages/python.md index 6934d944de0..557f420750b 100644 --- a/docs/src/languages/python.md +++ b/docs/src/languages/python.md @@ -80,7 +80,7 @@ Other built-in language servers are: - [Pyright](https://github.com/microsoft/pyright)—The basis for basedpyright. - [PyLSP](https://github.com/python-lsp/python-lsp-server)—A plugin-based language server that integrates with tools like `pycodestyle`, `autopep8`, and `yapf`. -These are disabled by default, but can be enabled in your settings. +These are disabled by default and are not included by `"..."`. Enable them by listing them by name in your settings. Configure language servers in Settings ({#kb zed::OpenSettings}) under Languages > Python, or add to your settings file: @@ -89,8 +89,8 @@ Configure language servers in Settings ({#kb zed::OpenSettings}) under Languages "languages": { "Python": { "language_servers": [ - // Enable ty, disable basedpyright, and enable all - // other registered language servers (ruff, pylsp, pyright). + // Enable ty, disable basedpyright, and enable all other + // language servers that are enabled by default (e.g. ruff). "ty", "!basedpyright", "..."