Add the ability for extensions to provide language settings (#10296)
This PR adds the ability for extensions to provide certain language settings via the language `config.toml`. These settings are then merged in with the rest of the settings when the language is loaded from the extension. The language settings that are available are: - `tab_size` - `hard_tabs` - `soft_wrap` Additionally, for bundled languages we moved these settings out of the `settings/default.json` and into their respective `config.toml`s . For languages currently provided by extensions, we are leaving the values in the `settings/default.json` temporarily until all released versions of Zed are able to load these settings from the extension. --- Along the way we ended up refactoring the `Settings::load` method slightly, introducing a new `SettingsSources` struct to better convey where the settings are being loaded from. This makes it easier to load settings from specific locations/sets of locations in an explicit way. Release Notes: - N/A --------- Co-authored-by: Max <max@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
co-authored by
Max
Max Brunsfeld
parent
4a3032c5e5
commit
7c5bc3c26f
@@ -38,6 +38,7 @@ use schemars::{
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use smol::future::FutureExt as _;
|
||||
use std::num::NonZeroU32;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
@@ -73,6 +74,8 @@ pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
|
||||
pub use text::LineEnding;
|
||||
pub use tree_sitter::{Parser, Tree};
|
||||
|
||||
use crate::language_settings::SoftWrap;
|
||||
|
||||
/// Initializes the `language` crate.
|
||||
///
|
||||
/// This should be called before making use of items from the create.
|
||||
@@ -99,6 +102,7 @@ lazy_static! {
|
||||
pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "Plain Text".into(),
|
||||
soft_wrap: Some(SoftWrap::PreferredLineLength),
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
@@ -576,6 +580,17 @@ pub struct LanguageConfig {
|
||||
/// The names of any Prettier plugins that should be used for this language.
|
||||
#[serde(default)]
|
||||
pub prettier_plugins: Vec<Arc<str>>,
|
||||
|
||||
/// Whether to indent lines using tab characters, as opposed to multiple
|
||||
/// spaces.
|
||||
#[serde(default)]
|
||||
pub hard_tabs: Option<bool>,
|
||||
/// How many columns a tab should occupy.
|
||||
#[serde(default)]
|
||||
pub tab_size: Option<NonZeroU32>,
|
||||
/// How to soft-wrap long lines of text.
|
||||
#[serde(default)]
|
||||
pub soft_wrap: Option<SoftWrap>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
|
||||
@@ -660,6 +675,9 @@ impl Default for LanguageConfig {
|
||||
prettier_parser_name: None,
|
||||
prettier_plugins: Default::default(),
|
||||
collapsed_placeholder: Default::default(),
|
||||
hard_tabs: Default::default(),
|
||||
tab_size: Default::default(),
|
||||
soft_wrap: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::language_settings::{AllLanguageSettingsContent, LanguageSettingsContent};
|
||||
use crate::{
|
||||
language_settings::all_language_settings, task_context::ContextProvider, CachedLspAdapter,
|
||||
File, Language, LanguageConfig, LanguageId, LanguageMatcher, LanguageServerName, LspAdapter,
|
||||
@@ -38,6 +39,7 @@ pub struct LanguageRegistry {
|
||||
struct LanguageRegistryState {
|
||||
next_language_server_id: usize,
|
||||
languages: Vec<Arc<Language>>,
|
||||
language_settings: AllLanguageSettingsContent,
|
||||
available_languages: Vec<AvailableLanguage>,
|
||||
grammars: HashMap<Arc<str>, AvailableGrammar>,
|
||||
lsp_adapters: HashMap<Arc<str>, Vec<Arc<CachedLspAdapter>>>,
|
||||
@@ -145,6 +147,7 @@ impl LanguageRegistry {
|
||||
languages: Vec::new(),
|
||||
available_languages: Vec::new(),
|
||||
grammars: Default::default(),
|
||||
language_settings: Default::default(),
|
||||
loading_languages: Default::default(),
|
||||
lsp_adapters: Default::default(),
|
||||
subscription: watch::channel(),
|
||||
@@ -338,6 +341,10 @@ impl LanguageRegistry {
|
||||
*state.subscription.0.borrow_mut() = ();
|
||||
}
|
||||
|
||||
pub fn language_settings(&self) -> AllLanguageSettingsContent {
|
||||
self.state.read().language_settings.clone()
|
||||
}
|
||||
|
||||
pub fn language_names(&self) -> Vec<String> {
|
||||
let state = self.state.read();
|
||||
let mut result = state
|
||||
@@ -854,6 +861,16 @@ impl LanguageRegistryState {
|
||||
if let Some(theme) = self.theme.as_ref() {
|
||||
language.set_theme(theme.syntax());
|
||||
}
|
||||
self.language_settings.languages.insert(
|
||||
language.name(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: language.config.tab_size,
|
||||
hard_tabs: language.config.hard_tabs,
|
||||
soft_wrap: language.config.soft_wrap,
|
||||
..Default::default()
|
||||
}
|
||||
.clone(),
|
||||
);
|
||||
self.languages.push(language);
|
||||
self.version += 1;
|
||||
*self.subscription.0.borrow_mut() = ();
|
||||
|
||||
@@ -10,7 +10,7 @@ use schemars::{
|
||||
JsonSchema,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsLocation};
|
||||
use settings::{Settings, SettingsLocation, SettingsSources};
|
||||
use std::{num::NonZeroU32, path::Path, sync::Arc};
|
||||
|
||||
impl<'a> Into<SettingsLocation<'a>> for &'a dyn File {
|
||||
@@ -119,7 +119,7 @@ pub struct CopilotSettings {
|
||||
}
|
||||
|
||||
/// The settings for all languages.
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AllLanguageSettingsContent {
|
||||
/// The settings for enabling/disabling features.
|
||||
#[serde(default)]
|
||||
@@ -140,7 +140,7 @@ pub struct AllLanguageSettingsContent {
|
||||
}
|
||||
|
||||
/// The settings for a particular language.
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct LanguageSettingsContent {
|
||||
/// How many columns a tab should occupy.
|
||||
///
|
||||
@@ -249,7 +249,7 @@ pub struct LanguageSettingsContent {
|
||||
}
|
||||
|
||||
/// The contents of the GitHub Copilot settings.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CopilotSettingsContent {
|
||||
/// A list of globs representing files that Copilot should be disabled for.
|
||||
#[serde(default)]
|
||||
@@ -257,7 +257,7 @@ pub struct CopilotSettingsContent {
|
||||
}
|
||||
|
||||
/// The settings for enabling/disabling features.
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct FeaturesContent {
|
||||
/// Whether the GitHub Copilot feature is enabled.
|
||||
@@ -473,11 +473,9 @@ impl settings::Settings for AllLanguageSettings {
|
||||
|
||||
type FileContent = AllLanguageSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_settings: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
let default_value = sources.default;
|
||||
|
||||
// A default is provided for all settings.
|
||||
let mut defaults: LanguageSettings =
|
||||
serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
|
||||
@@ -500,7 +498,8 @@ impl settings::Settings for AllLanguageSettings {
|
||||
.and_then(|c| c.disabled_globs.as_ref())
|
||||
.ok_or_else(Self::missing_default)?;
|
||||
|
||||
for user_settings in user_settings {
|
||||
let mut file_types: HashMap<Arc<str>, Vec<String>> = HashMap::default();
|
||||
for user_settings in sources.customizations() {
|
||||
if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
|
||||
copilot_enabled = copilot;
|
||||
}
|
||||
@@ -528,11 +527,8 @@ impl settings::Settings for AllLanguageSettings {
|
||||
user_language_settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut file_types: HashMap<Arc<str>, Vec<String>> = HashMap::default();
|
||||
for user_file_types in user_settings.iter().map(|s| &s.file_types) {
|
||||
for (language, suffixes) in user_file_types {
|
||||
for (language, suffixes) in &user_settings.file_types {
|
||||
file_types
|
||||
.entry(language.clone())
|
||||
.or_default()
|
||||
|
||||
Reference in New Issue
Block a user