mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-28 09:54:33 +00:00
Add language_servers
setting for customizing which language servers run (#10911)
This PR adds a new `language_servers` setting underneath the language settings. This setting controls which of the available language servers for a given language will run. The `language_servers` setting is an array of strings. Each item in the array must be either: - A language server ID (e.g., `"rust-analyzer"`, `"typescript-language-server"`, `"eslint"`, etc.) denoting a language server that should be enabled. - A language server ID prefixed with a `!` (e.g., `"!rust-analyzer"`, `"!typescript-language-server"`, `"!eslint"`, etc.) denoting a language server that should be disabled. - A `"..."` placeholder, which will be replaced by the remaining available language servers that haven't already been mentioned in the array. For example, to enable the Biome language server in place of the default TypeScript language server, you would add the following to your settings: ```json { "languages": { "TypeScript": { "language_servers": ["biome", "!typescript-language-server", "..."] } } } ``` More details can be found in #10906. Release Notes: - Added `language_servers` setting to language settings for customizing which language server(s) run for a given language.
This commit is contained in:
parent
68a1ad89bb
commit
cf67fc9055
5 changed files with 162 additions and 10 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -5454,6 +5454,7 @@ dependencies = [
|
|||
"globset",
|
||||
"gpui",
|
||||
"indoc",
|
||||
"itertools 0.11.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"lsp",
|
||||
|
|
|
@ -294,6 +294,10 @@
|
|||
"show_call_status_icon": true,
|
||||
// Whether to use language servers to provide code intelligence.
|
||||
"enable_language_server": true,
|
||||
// The list of language servers to use (or disable) for all languages.
|
||||
//
|
||||
// This is typically customized on a per-language basis.
|
||||
"language_servers": ["..."],
|
||||
// When to automatically save edited buffers. This setting can
|
||||
// take four values.
|
||||
//
|
||||
|
|
|
@ -34,11 +34,13 @@ fuzzy.workspace = true
|
|||
git.workspace = true
|
||||
globset.workspace = true
|
||||
gpui.workspace = true
|
||||
itertools.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
pulldown-cmark.workspace = true
|
||||
rand = { workspace = true, optional = true }
|
||||
regex.workspace = true
|
||||
rpc.workspace = true
|
||||
|
@ -50,15 +52,14 @@ similar = "1.3"
|
|||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
sum_tree.workspace = true
|
||||
task.workspace = true
|
||||
text.workspace = true
|
||||
theme.workspace = true
|
||||
tree-sitter-rust = { workspace = true, optional = true }
|
||||
tree-sitter-typescript = { workspace = true, optional = true }
|
||||
pulldown-cmark.workspace = true
|
||||
tree-sitter.workspace = true
|
||||
unicase = "2.6"
|
||||
util.workspace = true
|
||||
task.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
collections = { workspace = true, features = ["test-support"] }
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Provides `language`-related settings.
|
||||
|
||||
use crate::{File, Language};
|
||||
use crate::{File, Language, LanguageServerName};
|
||||
use anyhow::Result;
|
||||
use collections::{HashMap, HashSet};
|
||||
use globset::GlobMatcher;
|
||||
use gpui::AppContext;
|
||||
use itertools::{Either, Itertools};
|
||||
use schemars::{
|
||||
schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
|
||||
JsonSchema,
|
||||
|
@ -92,6 +93,13 @@ pub struct LanguageSettings {
|
|||
pub prettier: HashMap<String, serde_json::Value>,
|
||||
/// Whether to use language servers to provide code intelligence.
|
||||
pub enable_language_server: bool,
|
||||
/// The list of language servers to use (or disable) for this language.
|
||||
///
|
||||
/// This array should consist of language server IDs, as well as the following
|
||||
/// special tokens:
|
||||
/// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
|
||||
/// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
|
||||
pub language_servers: Vec<Arc<str>>,
|
||||
/// Controls whether Copilot provides suggestion immediately (true)
|
||||
/// or waits for a `copilot::Toggle` (false).
|
||||
pub show_copilot_suggestions: bool,
|
||||
|
@ -109,6 +117,53 @@ pub struct LanguageSettings {
|
|||
pub code_actions_on_format: HashMap<String, bool>,
|
||||
}
|
||||
|
||||
impl LanguageSettings {
|
||||
/// A token representing the rest of the available language servers.
|
||||
const REST_OF_LANGUAGE_SERVERS: &'static str = "...";
|
||||
|
||||
/// Returns the customized list of language servers from the list of
|
||||
/// available language servers.
|
||||
pub fn customized_language_servers(
|
||||
&self,
|
||||
available_language_servers: &[LanguageServerName],
|
||||
) -> Vec<LanguageServerName> {
|
||||
Self::resolve_language_servers(&self.language_servers, available_language_servers)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_language_servers(
|
||||
configured_language_servers: &[Arc<str>],
|
||||
available_language_servers: &[LanguageServerName],
|
||||
) -> Vec<LanguageServerName> {
|
||||
let (disabled_language_servers, enabled_language_servers): (Vec<Arc<str>>, Vec<Arc<str>>) =
|
||||
configured_language_servers.iter().partition_map(
|
||||
|language_server| match language_server.strip_prefix('!') {
|
||||
Some(disabled) => Either::Left(disabled.into()),
|
||||
None => Either::Right(language_server.clone()),
|
||||
},
|
||||
);
|
||||
|
||||
let rest = available_language_servers
|
||||
.into_iter()
|
||||
.filter(|&available_language_server| {
|
||||
!disabled_language_servers.contains(&&available_language_server.0)
|
||||
&& !enabled_language_servers.contains(&&available_language_server.0)
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
enabled_language_servers
|
||||
.into_iter()
|
||||
.flat_map(|language_server| {
|
||||
if language_server.as_ref() == Self::REST_OF_LANGUAGE_SERVERS {
|
||||
rest.clone()
|
||||
} else {
|
||||
vec![LanguageServerName(language_server.clone())]
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// The settings for [GitHub Copilot](https://github.com/features/copilot).
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CopilotSettings {
|
||||
|
@ -119,7 +174,7 @@ pub struct CopilotSettings {
|
|||
}
|
||||
|
||||
/// The settings for all languages.
|
||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AllLanguageSettingsContent {
|
||||
/// The settings for enabling/disabling features.
|
||||
#[serde(default)]
|
||||
|
@ -140,7 +195,7 @@ pub struct AllLanguageSettingsContent {
|
|||
}
|
||||
|
||||
/// The settings for a particular language.
|
||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct LanguageSettingsContent {
|
||||
/// How many columns a tab should occupy.
|
||||
///
|
||||
|
@ -211,6 +266,16 @@ pub struct LanguageSettingsContent {
|
|||
/// Default: true
|
||||
#[serde(default)]
|
||||
pub enable_language_server: Option<bool>,
|
||||
/// The list of language servers to use (or disable) for this language.
|
||||
///
|
||||
/// This array should consist of language server IDs, as well as the following
|
||||
/// special tokens:
|
||||
/// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
|
||||
/// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
|
||||
///
|
||||
/// Default: ["..."]
|
||||
#[serde(default)]
|
||||
pub language_servers: Option<Vec<Arc<str>>>,
|
||||
/// Controls whether Copilot provides suggestion immediately (true)
|
||||
/// or waits for a `copilot::Toggle` (false).
|
||||
///
|
||||
|
@ -257,7 +322,7 @@ pub struct CopilotSettingsContent {
|
|||
}
|
||||
|
||||
/// The settings for enabling/disabling features.
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct FeaturesContent {
|
||||
/// Whether the GitHub Copilot feature is enabled.
|
||||
|
@ -608,6 +673,12 @@ impl settings::Settings for AllLanguageSettings {
|
|||
}
|
||||
|
||||
fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
|
||||
fn merge<T>(target: &mut T, value: Option<T>) {
|
||||
if let Some(value) = value {
|
||||
*target = value;
|
||||
}
|
||||
}
|
||||
|
||||
merge(&mut settings.tab_size, src.tab_size);
|
||||
merge(&mut settings.hard_tabs, src.hard_tabs);
|
||||
merge(&mut settings.soft_wrap, src.soft_wrap);
|
||||
|
@ -642,6 +713,7 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
|
|||
&mut settings.enable_language_server,
|
||||
src.enable_language_server,
|
||||
);
|
||||
merge(&mut settings.language_servers, src.language_servers.clone());
|
||||
merge(
|
||||
&mut settings.show_copilot_suggestions,
|
||||
src.show_copilot_suggestions,
|
||||
|
@ -652,9 +724,70 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
|
|||
src.extend_comment_on_newline,
|
||||
);
|
||||
merge(&mut settings.inlay_hints, src.inlay_hints);
|
||||
fn merge<T>(target: &mut T, value: Option<T>) {
|
||||
if let Some(value) = value {
|
||||
*target = value;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_resolve_language_servers() {
|
||||
fn language_server_names(names: &[&str]) -> Vec<LanguageServerName> {
|
||||
names
|
||||
.into_iter()
|
||||
.copied()
|
||||
.map(|name| LanguageServerName(name.into()))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
let available_language_servers = language_server_names(&[
|
||||
"typescript-language-server",
|
||||
"biome",
|
||||
"deno",
|
||||
"eslint",
|
||||
"tailwind",
|
||||
]);
|
||||
|
||||
// A value of just `["..."]` is the same as taking all of the available language servers.
|
||||
assert_eq!(
|
||||
LanguageSettings::resolve_language_servers(
|
||||
&[LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()],
|
||||
&available_language_servers,
|
||||
),
|
||||
available_language_servers
|
||||
);
|
||||
|
||||
// Referencing one of the available language servers will change its order.
|
||||
assert_eq!(
|
||||
LanguageSettings::resolve_language_servers(
|
||||
&[
|
||||
"biome".into(),
|
||||
LanguageSettings::REST_OF_LANGUAGE_SERVERS.into(),
|
||||
"deno".into()
|
||||
],
|
||||
&available_language_servers
|
||||
),
|
||||
language_server_names(&[
|
||||
"biome",
|
||||
"typescript-language-server",
|
||||
"eslint",
|
||||
"tailwind",
|
||||
"deno",
|
||||
])
|
||||
);
|
||||
|
||||
// Negating an available language server removes it from the list.
|
||||
assert_eq!(
|
||||
LanguageSettings::resolve_language_servers(
|
||||
&[
|
||||
"deno".into(),
|
||||
"!typescript-language-server".into(),
|
||||
"!biome".into(),
|
||||
LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
|
||||
],
|
||||
&available_language_servers
|
||||
),
|
||||
language_server_names(&["deno", "eslint", "tailwind"])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3060,7 +3060,20 @@ impl Project {
|
|||
return;
|
||||
}
|
||||
|
||||
for adapter in self.languages.clone().lsp_adapters(&language) {
|
||||
let available_lsp_adapters = self.languages.clone().lsp_adapters(&language);
|
||||
let available_language_servers = available_lsp_adapters
|
||||
.iter()
|
||||
.map(|lsp_adapter| lsp_adapter.name.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let enabled_language_servers =
|
||||
settings.customized_language_servers(&available_language_servers);
|
||||
|
||||
for adapter in available_lsp_adapters {
|
||||
if !enabled_language_servers.contains(&adapter.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.start_language_server(worktree, adapter.clone(), language.clone(), cx);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue