mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 06:12:25 +03:00
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:
parent
4a3032c5e5
commit
7c5bc3c26f
@ -556,18 +556,10 @@
|
||||
"C": {
|
||||
"format_on_save": "off"
|
||||
},
|
||||
"Plain Text": {
|
||||
"soft_wrap": "preferred_line_length"
|
||||
},
|
||||
"Elixir": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"Gleam": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"Go": {
|
||||
"tab_size": 4,
|
||||
"hard_tabs": true,
|
||||
"code_actions_on_format": {
|
||||
"source.organizeImports": true
|
||||
}
|
||||
@ -575,34 +567,6 @@
|
||||
"Make": {
|
||||
"hard_tabs": true
|
||||
},
|
||||
"Markdown": {
|
||||
"tab_size": 2,
|
||||
"soft_wrap": "preferred_line_length"
|
||||
},
|
||||
"JavaScript": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"Terraform": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"TypeScript": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"TSX": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"YAML": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"JSON": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"OCaml": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"OCaml Interface": {
|
||||
"tab_size": 2
|
||||
},
|
||||
"Prisma": {
|
||||
"tab_size": 2
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub enum ZedDotDevModel {
|
||||
@ -332,13 +332,12 @@ impl Settings for AssistantSettings {
|
||||
type FileContent = AssistantSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
let mut settings = AssistantSettings::default();
|
||||
|
||||
for value in [default_value].iter().chain(user_values) {
|
||||
for value in sources.defaults_and_customizations() {
|
||||
let value = value.upgrade();
|
||||
merge(&mut settings.enabled, value.enabled);
|
||||
merge(&mut settings.button, value.button);
|
||||
|
@ -17,7 +17,7 @@ use serde::Deserialize;
|
||||
use serde_derive::Serialize;
|
||||
use smol::io::AsyncReadExt;
|
||||
|
||||
use settings::{Settings, SettingsStore};
|
||||
use settings::{Settings, SettingsSources, SettingsStore};
|
||||
use smol::{fs::File, process::Command};
|
||||
|
||||
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
|
||||
@ -91,13 +91,12 @@ impl Settings for AutoUpdateSetting {
|
||||
|
||||
type FileContent = AutoUpdateSettingOverride;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
Ok(Self(
|
||||
Self::json_merge(default_value, user_values)?
|
||||
sources
|
||||
.release_channel
|
||||
.or(sources.user)
|
||||
.unwrap_or(sources.default)
|
||||
.0
|
||||
.ok_or_else(Self::missing_default)?,
|
||||
))
|
||||
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct CallSettings {
|
||||
@ -29,14 +29,7 @@ impl Settings for CallSettings {
|
||||
|
||||
type FileContent = CallSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_cx: &mut AppContext,
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use release_channel::{AppVersion, ReleaseChannel};
|
||||
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use settings::{Settings, SettingsSources, SettingsStore};
|
||||
use std::fmt;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
@ -97,15 +97,11 @@ impl Settings for ClientSettings {
|
||||
|
||||
type FileContent = ClientSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self>
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut result = Self::load_via_json_merge(default_value, user_values)?;
|
||||
let mut result = sources.json_merge::<Self>()?;
|
||||
if let Some(server_url) = &*ZED_SERVER_URL {
|
||||
result.server_url = server_url.clone()
|
||||
}
|
||||
@ -427,21 +423,19 @@ impl settings::Settings for TelemetrySettings {
|
||||
|
||||
type FileContent = TelemetrySettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
Ok(Self {
|
||||
diagnostics: user_values.first().and_then(|v| v.diagnostics).unwrap_or(
|
||||
default_value
|
||||
diagnostics: sources.user.as_ref().and_then(|v| v.diagnostics).unwrap_or(
|
||||
sources
|
||||
.default
|
||||
.diagnostics
|
||||
.ok_or_else(Self::missing_default)?,
|
||||
),
|
||||
metrics: user_values
|
||||
.first()
|
||||
metrics: sources
|
||||
.user
|
||||
.as_ref()
|
||||
.and_then(|v| v.metrics)
|
||||
.unwrap_or(default_value.metrics.ok_or_else(Self::missing_default)?),
|
||||
.unwrap_or(sources.default.metrics.ok_or_else(Self::missing_default)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use anyhow;
|
||||
use gpui::Pixels;
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use workspace::dock::DockPosition;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@ -53,48 +53,52 @@ pub struct MessageEditorSettings {
|
||||
|
||||
impl Settings for CollaborationPanelSettings {
|
||||
const KEY: Option<&'static str> = Some("collaboration_panel");
|
||||
|
||||
type FileContent = PanelSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings for ChatPanelSettings {
|
||||
const KEY: Option<&'static str> = Some("chat_panel");
|
||||
|
||||
type FileContent = PanelSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings for NotificationPanelSettings {
|
||||
const KEY: Option<&'static str> = Some("notification_panel");
|
||||
|
||||
type FileContent = PanelSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings for MessageEditorSettings {
|
||||
const KEY: Option<&'static str> = Some("message_editor");
|
||||
|
||||
type FileContent = MessageEditorSettings;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ProjectDiagnosticsSettings {
|
||||
@ -15,18 +18,11 @@ pub struct ProjectDiagnosticsSettingsContent {
|
||||
include_warnings: Option<bool>,
|
||||
}
|
||||
|
||||
impl settings::Settings for ProjectDiagnosticsSettings {
|
||||
impl Settings for ProjectDiagnosticsSettings {
|
||||
const KEY: Option<&'static str> = Some("diagnostics");
|
||||
type FileContent = ProjectDiagnosticsSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_cx: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct EditorSettings {
|
||||
@ -224,10 +225,9 @@ impl Settings for EditorSettings {
|
||||
type FileContent = EditorSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut gpui::AppContext,
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use collections::HashMap;
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)]
|
||||
@ -26,14 +26,7 @@ impl Settings for ExtensionSettings {
|
||||
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(
|
||||
_default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_cx: &mut AppContext,
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(user_values.get(0).copied().cloned().unwrap_or_default())
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _cx: &mut AppContext) -> Result<Self> {
|
||||
Ok(sources.user.cloned().unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use editor::Editor;
|
||||
use gpui::{actions, AppContext, ViewContext, WindowContext};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
path::{Path, PathBuf},
|
||||
@ -50,12 +50,8 @@ impl settings::Settings for JournalSettings {
|
||||
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(
|
||||
defaults: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(defaults, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -2,12 +2,13 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use futures::StreamExt;
|
||||
use gpui::AppContext;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use smol::{fs, fs::File};
|
||||
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
|
||||
use util::{
|
||||
@ -31,15 +32,8 @@ impl Settings for DenoSettings {
|
||||
|
||||
type FileContent = DenoSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut gpui::AppContext,
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AsyncAppContext, Task};
|
||||
use gpui::{AppContext, AsyncAppContext, Task};
|
||||
pub use language::*;
|
||||
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
|
||||
use project::project_settings::ProjectSettings;
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use smol::fs::{self, File};
|
||||
use std::{
|
||||
any::Any,
|
||||
@ -56,15 +56,8 @@ impl Settings for ElixirSettings {
|
||||
|
||||
type FileContent = ElixirSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut gpui::AppContext,
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ brackets = [
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
||||
tab_size = 2
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||
|
||||
[overrides.string]
|
||||
|
@ -11,3 +11,5 @@ brackets = [
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
||||
tab_size = 4
|
||||
hard_tabs = true
|
||||
|
@ -15,6 +15,7 @@ brackets = [
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
||||
word_characters = ["$", "#"]
|
||||
tab_size = 2
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||
prettier_parser_name = "babel"
|
||||
|
||||
|
@ -9,3 +9,4 @@ brackets = [
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
]
|
||||
prettier_parser_name = "json"
|
||||
tab_size = 2
|
||||
|
@ -1,11 +1,12 @@
|
||||
use anyhow::Context;
|
||||
use gpui::AppContext;
|
||||
use gpui::{AppContext, BorrowAppContext};
|
||||
pub use language::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use rust_embed::RustEmbed;
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use smol::stream::StreamExt;
|
||||
use std::{str, sync::Arc};
|
||||
use util::asset_str;
|
||||
use util::{asset_str, ResultExt};
|
||||
|
||||
use crate::{elixir::elixir_task_context, rust::RustContextProvider};
|
||||
|
||||
@ -327,6 +328,27 @@ pub fn init(
|
||||
"Svelte".into(),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
);
|
||||
|
||||
let mut subscription = languages.subscribe();
|
||||
let mut prev_language_settings = languages.language_settings();
|
||||
|
||||
cx.spawn(|cx| async move {
|
||||
while subscription.next().await.is_some() {
|
||||
let language_settings = languages.language_settings();
|
||||
if language_settings != prev_language_settings {
|
||||
cx.update(|cx| {
|
||||
cx.update_global(|settings: &mut SettingsStore, cx| {
|
||||
settings
|
||||
.set_extension_settings(language_settings.clone(), cx)
|
||||
.log_err();
|
||||
});
|
||||
})?;
|
||||
prev_language_settings = language_settings;
|
||||
}
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -12,3 +12,6 @@ brackets = [
|
||||
{ start = "`", end = "`", close = false, newline = false },
|
||||
]
|
||||
prettier_parser_name = "markdown"
|
||||
|
||||
tab_size = 2
|
||||
soft_wrap = "preferred_line_length"
|
||||
|
@ -10,3 +10,4 @@ brackets = [
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true }
|
||||
]
|
||||
tab_size = 2
|
||||
|
@ -11,3 +11,4 @@ brackets = [
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }
|
||||
]
|
||||
tab_size = 2
|
||||
|
@ -12,3 +12,4 @@ brackets = [
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
||||
tab_size = 2
|
||||
|
@ -16,6 +16,7 @@ brackets = [
|
||||
word_characters = ["#", "$"]
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||
prettier_parser_name = "typescript"
|
||||
tab_size = 2
|
||||
|
||||
[overrides.element]
|
||||
line_comments = { remove = true }
|
||||
|
@ -15,3 +15,4 @@ brackets = [
|
||||
]
|
||||
word_characters = ["#", "$"]
|
||||
prettier_parser_name = "typescript"
|
||||
tab_size = 2
|
||||
|
@ -11,3 +11,4 @@ brackets = [
|
||||
|
||||
increase_indent_pattern = ":\\s*[|>]?\\s*$"
|
||||
prettier_parser_name = "yaml"
|
||||
tab_size = 2
|
||||
|
@ -2,7 +2,7 @@ use collections::HashMap;
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
@ -61,10 +61,9 @@ impl Settings for ProjectSettings {
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use anyhow;
|
||||
use gpui::Pixels;
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@ -62,10 +62,9 @@ impl Settings for ProjectPanelSettings {
|
||||
type FileContent = ProjectPanelSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ use util::asset_str;
|
||||
|
||||
pub use keymap_file::KeymapFile;
|
||||
pub use settings_file::*;
|
||||
pub use settings_store::{Settings, SettingsJsonSchemaParams, SettingsLocation, SettingsStore};
|
||||
pub use settings_store::{
|
||||
Settings, SettingsJsonSchemaParams, SettingsLocation, SettingsSources, SettingsStore,
|
||||
};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../../assets"]
|
||||
|
@ -29,14 +29,7 @@ pub trait Settings: 'static + Send + Sync {
|
||||
|
||||
/// The logic for combining together values from one or more JSON files into the
|
||||
/// final value for this setting.
|
||||
///
|
||||
/// The user values are ordered from least specific (the global settings file)
|
||||
/// to most specific (the innermost local settings file).
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
cx: &mut AppContext,
|
||||
) -> Result<Self>
|
||||
fn load(sources: SettingsSources<Self::FileContent>, cx: &mut AppContext) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
@ -48,31 +41,6 @@ pub trait Settings: 'static + Send + Sync {
|
||||
generator.root_schema_for::<Self::FileContent>()
|
||||
}
|
||||
|
||||
fn json_merge(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
) -> Result<Self::FileContent> {
|
||||
let mut merged = serde_json::Value::Null;
|
||||
for value in [default_value].iter().chain(user_values) {
|
||||
merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
|
||||
}
|
||||
Ok(serde_json::from_value(merged)?)
|
||||
}
|
||||
|
||||
fn load_via_json_merge(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: DeserializeOwned,
|
||||
{
|
||||
let mut merged = serde_json::Value::Null;
|
||||
for value in [default_value].iter().chain(user_values) {
|
||||
merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
|
||||
}
|
||||
Ok(serde_json::from_value(merged)?)
|
||||
}
|
||||
|
||||
fn missing_default() -> anyhow::Error {
|
||||
anyhow::anyhow!("missing default")
|
||||
}
|
||||
@ -119,6 +87,48 @@ pub trait Settings: 'static + Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct SettingsSources<'a, T> {
|
||||
/// The default Zed settings.
|
||||
pub default: &'a T,
|
||||
/// Settings provided by extensions.
|
||||
pub extensions: Option<&'a T>,
|
||||
/// The user settings.
|
||||
pub user: Option<&'a T>,
|
||||
/// The user settings for the current release channel.
|
||||
pub release_channel: Option<&'a T>,
|
||||
/// The project settings, ordered from least specific to most specific.
|
||||
pub project: &'a [&'a T],
|
||||
}
|
||||
|
||||
impl<'a, T: Serialize> SettingsSources<'a, T> {
|
||||
/// Returns an iterator over the default settings as well as all settings customizations.
|
||||
pub fn defaults_and_customizations(&self) -> impl Iterator<Item = &T> {
|
||||
[self.default].into_iter().chain(self.customizations())
|
||||
}
|
||||
|
||||
/// Returns an iterator over all of the settings customizations.
|
||||
pub fn customizations(&self) -> impl Iterator<Item = &T> {
|
||||
self.extensions
|
||||
.into_iter()
|
||||
.chain(self.user)
|
||||
.chain(self.release_channel)
|
||||
.chain(self.project.iter().copied())
|
||||
}
|
||||
|
||||
/// Returns the settings after performing a JSON merge of the customizations into the
|
||||
/// default settings.
|
||||
///
|
||||
/// More-specific customizations win out over the less-specific ones.
|
||||
pub fn json_merge<O: DeserializeOwned>(&self) -> Result<O> {
|
||||
let mut merged = serde_json::Value::Null;
|
||||
for value in self.defaults_and_customizations() {
|
||||
merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
|
||||
}
|
||||
Ok(serde_json::from_value(merged)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SettingsLocation<'a> {
|
||||
pub worktree_id: usize,
|
||||
@ -136,6 +146,7 @@ pub struct SettingsStore {
|
||||
setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
|
||||
raw_default_settings: serde_json::Value,
|
||||
raw_user_settings: serde_json::Value,
|
||||
raw_extension_settings: serde_json::Value,
|
||||
raw_local_settings: BTreeMap<(usize, Arc<Path>), serde_json::Value>,
|
||||
tab_size_callback: Option<(
|
||||
TypeId,
|
||||
@ -151,6 +162,7 @@ impl Default for SettingsStore {
|
||||
setting_values: Default::default(),
|
||||
raw_default_settings: serde_json::json!({}),
|
||||
raw_user_settings: serde_json::json!({}),
|
||||
raw_extension_settings: serde_json::json!({}),
|
||||
raw_local_settings: Default::default(),
|
||||
tab_size_callback: Default::default(),
|
||||
}
|
||||
@ -169,8 +181,7 @@ trait AnySettingValue: 'static + Send + Sync {
|
||||
fn deserialize_setting(&self, json: &serde_json::Value) -> Result<DeserializedSetting>;
|
||||
fn load_setting(
|
||||
&self,
|
||||
default_value: &DeserializedSetting,
|
||||
custom: &[DeserializedSetting],
|
||||
sources: SettingsSources<DeserializedSetting>,
|
||||
cx: &mut AppContext,
|
||||
) -> Result<Box<dyn Any>>;
|
||||
fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
|
||||
@ -204,29 +215,35 @@ impl SettingsStore {
|
||||
.deserialize_setting(&self.raw_default_settings)
|
||||
.log_err()
|
||||
{
|
||||
let mut user_values_stack = Vec::new();
|
||||
|
||||
if let Some(user_settings) = setting_value
|
||||
let user_value = setting_value
|
||||
.deserialize_setting(&self.raw_user_settings)
|
||||
.log_err()
|
||||
{
|
||||
user_values_stack = vec![user_settings];
|
||||
}
|
||||
.log_err();
|
||||
|
||||
let mut release_channel_value = None;
|
||||
if let Some(release_settings) = &self
|
||||
.raw_user_settings
|
||||
.get(release_channel::RELEASE_CHANNEL.dev_name())
|
||||
{
|
||||
if let Some(release_settings) = setting_value
|
||||
release_channel_value = setting_value
|
||||
.deserialize_setting(release_settings)
|
||||
.log_err()
|
||||
{
|
||||
user_values_stack.push(release_settings);
|
||||
}
|
||||
.log_err();
|
||||
}
|
||||
|
||||
let extension_value = setting_value
|
||||
.deserialize_setting(&self.raw_extension_settings)
|
||||
.log_err();
|
||||
|
||||
if let Some(setting) = setting_value
|
||||
.load_setting(&default_settings, &user_values_stack, cx)
|
||||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
release_channel: release_channel_value.as_ref(),
|
||||
extensions: extension_value.as_ref(),
|
||||
user: user_value.as_ref(),
|
||||
project: &[],
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.context("A default setting must be added to the `default.json` file")
|
||||
.log_err()
|
||||
{
|
||||
@ -425,6 +442,21 @@ impl SettingsStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_extension_settings<T: Serialize>(
|
||||
&mut self,
|
||||
content: T,
|
||||
cx: &mut AppContext,
|
||||
) -> Result<()> {
|
||||
let settings: serde_json::Value = serde_json::to_value(content)?;
|
||||
if settings.is_object() {
|
||||
self.raw_extension_settings = settings;
|
||||
self.recompute_values(None, cx)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("settings must be an object"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Add or remove a set of local settings via a JSON string.
|
||||
pub fn clear_local_settings(&mut self, root_id: usize, cx: &mut AppContext) -> Result<()> {
|
||||
self.raw_local_settings.retain(|k, _| k.0 != root_id);
|
||||
@ -551,22 +583,20 @@ impl SettingsStore {
|
||||
cx: &mut AppContext,
|
||||
) -> Result<()> {
|
||||
// Reload the global and local values for every setting.
|
||||
let mut user_settings_stack = Vec::<DeserializedSetting>::new();
|
||||
let mut project_settings_stack = Vec::<DeserializedSetting>::new();
|
||||
let mut paths_stack = Vec::<Option<(usize, &Path)>>::new();
|
||||
for setting_value in self.setting_values.values_mut() {
|
||||
let default_settings = setting_value.deserialize_setting(&self.raw_default_settings)?;
|
||||
|
||||
user_settings_stack.clear();
|
||||
paths_stack.clear();
|
||||
let extension_settings = setting_value
|
||||
.deserialize_setting(&self.raw_extension_settings)
|
||||
.log_err();
|
||||
|
||||
if let Some(user_settings) = setting_value
|
||||
let user_settings = setting_value
|
||||
.deserialize_setting(&self.raw_user_settings)
|
||||
.log_err()
|
||||
{
|
||||
user_settings_stack.push(user_settings);
|
||||
paths_stack.push(None);
|
||||
}
|
||||
.log_err();
|
||||
|
||||
let mut release_channel_settings = None;
|
||||
if let Some(release_settings) = &self
|
||||
.raw_user_settings
|
||||
.get(release_channel::RELEASE_CHANNEL.dev_name())
|
||||
@ -575,15 +605,25 @@ impl SettingsStore {
|
||||
.deserialize_setting(release_settings)
|
||||
.log_err()
|
||||
{
|
||||
user_settings_stack.push(release_settings);
|
||||
paths_stack.push(None);
|
||||
release_channel_settings = Some(release_settings);
|
||||
}
|
||||
}
|
||||
|
||||
// If the global settings file changed, reload the global value for the field.
|
||||
project_settings_stack.clear();
|
||||
paths_stack.clear();
|
||||
if changed_local_path.is_none() {
|
||||
if let Some(value) = setting_value
|
||||
.load_setting(&default_settings, &user_settings_stack, cx)
|
||||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
extensions: extension_settings.as_ref(),
|
||||
user: user_settings.as_ref(),
|
||||
release_channel: release_channel_settings.as_ref(),
|
||||
project: &[],
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.log_err()
|
||||
{
|
||||
setting_value.set_global_value(value);
|
||||
@ -597,7 +637,7 @@ impl SettingsStore {
|
||||
if let Some((prev_root_id, prev_path)) = prev_entry {
|
||||
if root_id != prev_root_id || !path.starts_with(prev_path) {
|
||||
paths_stack.pop();
|
||||
user_settings_stack.pop();
|
||||
project_settings_stack.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -608,7 +648,7 @@ impl SettingsStore {
|
||||
setting_value.deserialize_setting(local_settings).log_err()
|
||||
{
|
||||
paths_stack.push(Some((*root_id, path.as_ref())));
|
||||
user_settings_stack.push(local_settings);
|
||||
project_settings_stack.push(local_settings);
|
||||
|
||||
// If a local settings file changed, then avoid recomputing local
|
||||
// settings for any path outside of that directory.
|
||||
@ -619,7 +659,16 @@ impl SettingsStore {
|
||||
}
|
||||
|
||||
if let Some(value) = setting_value
|
||||
.load_setting(&default_settings, &user_settings_stack, cx)
|
||||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
extensions: extension_settings.as_ref(),
|
||||
user: user_settings.as_ref(),
|
||||
release_channel: release_channel_settings.as_ref(),
|
||||
project: &project_settings_stack.iter().collect::<Vec<_>>(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.log_err()
|
||||
{
|
||||
setting_value.set_local_value(*root_id, path.clone(), value);
|
||||
@ -660,16 +709,30 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
|
||||
|
||||
fn load_setting(
|
||||
&self,
|
||||
default_value: &DeserializedSetting,
|
||||
user_values: &[DeserializedSetting],
|
||||
values: SettingsSources<DeserializedSetting>,
|
||||
cx: &mut AppContext,
|
||||
) -> Result<Box<dyn Any>> {
|
||||
let default_value = default_value.0.downcast_ref::<T::FileContent>().unwrap();
|
||||
let values: SmallVec<[&T::FileContent; 6]> = user_values
|
||||
.iter()
|
||||
.map(|value| value.0.downcast_ref().unwrap())
|
||||
.collect();
|
||||
Ok(Box::new(T::load(default_value, &values, cx)?))
|
||||
Ok(Box::new(T::load(
|
||||
SettingsSources {
|
||||
default: values.default.0.downcast_ref::<T::FileContent>().unwrap(),
|
||||
extensions: values
|
||||
.extensions
|
||||
.map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
|
||||
user: values
|
||||
.user
|
||||
.map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
|
||||
release_channel: values
|
||||
.release_channel
|
||||
.map(|value| value.0.downcast_ref::<T::FileContent>().unwrap()),
|
||||
project: values
|
||||
.project
|
||||
.iter()
|
||||
.map(|value| value.0.downcast_ref().unwrap())
|
||||
.collect::<SmallVec<[_; 3]>>()
|
||||
.as_slice(),
|
||||
},
|
||||
cx,
|
||||
)?))
|
||||
}
|
||||
|
||||
fn deserialize_setting(&self, mut json: &serde_json::Value) -> Result<DeserializedSetting> {
|
||||
@ -1277,12 +1340,8 @@ mod tests {
|
||||
const KEY: Option<&'static str> = Some("user");
|
||||
type FileContent = UserSettingsJson;
|
||||
|
||||
fn load(
|
||||
default_value: &UserSettingsJson,
|
||||
user_values: &[&UserSettingsJson],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1293,12 +1352,8 @@ mod tests {
|
||||
const KEY: Option<&'static str> = Some("turbo");
|
||||
type FileContent = Option<bool>;
|
||||
|
||||
fn load(
|
||||
default_value: &Option<bool>,
|
||||
user_values: &[&Option<bool>],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1321,12 +1376,8 @@ mod tests {
|
||||
|
||||
type FileContent = MultiKeySettingsJson;
|
||||
|
||||
fn load(
|
||||
default_value: &MultiKeySettingsJson,
|
||||
user_values: &[&MultiKeySettingsJson],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1354,12 +1405,8 @@ mod tests {
|
||||
|
||||
type FileContent = JournalSettingsJson;
|
||||
|
||||
fn load(
|
||||
default_value: &JournalSettingsJson,
|
||||
user_values: &[&JournalSettingsJson],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1380,8 +1427,8 @@ mod tests {
|
||||
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(default_value: &Self, user_values: &[&Self], _: &mut AppContext) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Default)]
|
||||
pub(crate) struct TaskSettings {
|
||||
@ -13,22 +14,15 @@ pub(crate) struct TaskSettingsContent {
|
||||
show_status_indicator: Option<bool>,
|
||||
}
|
||||
|
||||
impl settings::Settings for TaskSettings {
|
||||
impl Settings for TaskSettings {
|
||||
const KEY: Option<&'static str> = Some("task");
|
||||
|
||||
type FileContent = TaskSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> gpui::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let this = Self::json_merge(default_value, user_values)?;
|
||||
Ok(Self {
|
||||
show_status_indicator: this.show_status_indicator.unwrap_or(true),
|
||||
})
|
||||
) -> gpui::Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use schemars::{
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use settings::SettingsJsonSchemaParams;
|
||||
use settings::{SettingsJsonSchemaParams, SettingsSources};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
@ -171,12 +171,12 @@ impl settings::Settings for TerminalSettings {
|
||||
type FileContent = TerminalSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
|
||||
fn json_schema(
|
||||
generator: &mut SchemaGenerator,
|
||||
params: &SettingsJsonSchemaParams,
|
||||
|
@ -14,7 +14,7 @@ use schemars::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use settings::{Settings, SettingsJsonSchemaParams};
|
||||
use settings::{Settings, SettingsJsonSchemaParams, SettingsSources};
|
||||
use std::sync::Arc;
|
||||
use util::ResultExt as _;
|
||||
|
||||
@ -316,14 +316,11 @@ impl settings::Settings for ThemeSettings {
|
||||
|
||||
type FileContent = ThemeSettingsContent;
|
||||
|
||||
fn load(
|
||||
defaults: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
cx: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
fn load(sources: SettingsSources<Self::FileContent>, cx: &mut AppContext) -> Result<Self> {
|
||||
let themes = ThemeRegistry::default_global(cx);
|
||||
let system_appearance = SystemAppearance::default_global(cx);
|
||||
|
||||
let defaults = sources.default;
|
||||
let mut this = Self {
|
||||
ui_font_size: defaults.ui_font_size.unwrap().into(),
|
||||
ui_font: Font {
|
||||
@ -348,15 +345,15 @@ impl settings::Settings for ThemeSettings {
|
||||
theme_overrides: None,
|
||||
};
|
||||
|
||||
for value in user_values.iter().copied().cloned() {
|
||||
if let Some(value) = value.buffer_font_family {
|
||||
for value in sources.user.into_iter().chain(sources.release_channel) {
|
||||
if let Some(value) = value.buffer_font_family.clone() {
|
||||
this.buffer_font.family = value.into();
|
||||
}
|
||||
if let Some(value) = value.buffer_font_features {
|
||||
this.buffer_font.features = value;
|
||||
}
|
||||
|
||||
if let Some(value) = value.ui_font_family {
|
||||
if let Some(value) = value.ui_font_family.clone() {
|
||||
this.ui_font.family = value.into();
|
||||
}
|
||||
if let Some(value) = value.ui_font_features {
|
||||
@ -373,7 +370,7 @@ impl settings::Settings for ThemeSettings {
|
||||
}
|
||||
}
|
||||
|
||||
this.theme_overrides = value.theme_overrides;
|
||||
this.theme_overrides = value.theme_overrides.clone();
|
||||
this.apply_theme_overrides();
|
||||
|
||||
merge(&mut this.ui_font_size, value.ui_font_size.map(Into::into));
|
||||
|
@ -35,7 +35,7 @@ use replace::multi_replace;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde_derive::Serialize;
|
||||
use settings::{update_settings_file, Settings, SettingsStore};
|
||||
use settings::{update_settings_file, Settings, SettingsSources, SettingsStore};
|
||||
use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState};
|
||||
use std::{ops::Range, sync::Arc};
|
||||
use surrounds::{add_surrounds, change_surrounds, delete_surrounds};
|
||||
@ -779,13 +779,9 @@ impl Settings for VimModeSetting {
|
||||
|
||||
type FileContent = Option<bool>;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(user_values.iter().rev().find_map(|v| **v).unwrap_or(
|
||||
default_value.ok_or_else(Self::missing_default)?,
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
Ok(Self(sources.user.copied().flatten().unwrap_or(
|
||||
sources.default.ok_or_else(Self::missing_default)?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
@ -824,11 +820,7 @@ impl Settings for VimSettings {
|
||||
|
||||
type FileContent = VimSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
|
||||
///
|
||||
@ -70,16 +70,12 @@ impl Settings for BaseKeymap {
|
||||
type FileContent = Option<Self>;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(user_values
|
||||
.first()
|
||||
.and_then(|v| **v)
|
||||
.unwrap_or(default_value.unwrap()))
|
||||
) -> anyhow::Result<Self> {
|
||||
if let Some(Some(user_value)) = sources.user.copied() {
|
||||
return Ok(user_value);
|
||||
}
|
||||
sources.default.ok_or_else(Self::missing_default)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use gpui::{
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@ -76,12 +76,8 @@ impl Settings for ItemSettings {
|
||||
|
||||
type FileContent = ItemSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut AppContext,
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WorkspaceSettings {
|
||||
@ -63,12 +65,8 @@ impl Settings for WorkspaceSettings {
|
||||
|
||||
type FileContent = WorkspaceSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,11 +75,7 @@ impl Settings for TabBarSettings {
|
||||
|
||||
type FileContent = TabBarSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct WorktreeSettings {
|
||||
@ -31,10 +31,9 @@ impl Settings for WorktreeSettings {
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
sources: SettingsSources<Self::FileContent>,
|
||||
_: &mut AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
sources.json_merge()
|
||||
}
|
||||
}
|
||||
|
@ -607,6 +607,7 @@ pub fn handle_keymap_file_changes(
|
||||
cx.observe_global::<SettingsStore>(move |cx| {
|
||||
let new_base_keymap = *BaseKeymap::get_global(cx);
|
||||
let new_vim_enabled = VimModeSetting::get_global(cx).0;
|
||||
|
||||
if new_base_keymap != old_base_keymap || new_vim_enabled != old_vim_enabled {
|
||||
old_base_keymap = new_base_keymap;
|
||||
old_vim_enabled = new_vim_enabled;
|
||||
@ -3062,6 +3063,7 @@ mod tests {
|
||||
let mut app_state = AppState::test(cx);
|
||||
|
||||
let state = Arc::get_mut(&mut app_state).unwrap();
|
||||
env_logger::try_init().ok();
|
||||
|
||||
state.build_window_options = build_window_options;
|
||||
theme::init(theme::LoadThemes::JustBase, cx);
|
||||
|
@ -9,3 +9,4 @@ brackets = [
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
||||
tab_size = 2
|
||||
|
@ -7,3 +7,4 @@ brackets = [
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true }
|
||||
]
|
||||
tab_size = 2
|
||||
|
Loading…
Reference in New Issue
Block a user