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:
Marshall Bowers 2024-04-08 19:17:12 -04:00 committed by GitHub
parent 4a3032c5e5
commit 7c5bc3c26f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 349 additions and 338 deletions

View file

@ -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()