1456 lines · 47.5 KB · Rust
RawDownload
use anyhow::{anyhow, Context, Result};use collections::{btree_map, hash_map, BTreeMap, HashMap};use gpui::{AppContext, AsyncAppContext, BorrowAppContext, Global, UpdateGlobal};use lazy_static::lazy_static;use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};use serde::{de::DeserializeOwned, Deserialize as _, Serialize};use smallvec::SmallVec;use std::{    any::{type_name, Any, TypeId},    fmt::Debug,    ops::Range,    path::Path,    str,    sync::Arc,};use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _}; /// A value that can be defined as a user setting.////// Settings can be loaded from a combination of multiple JSON files.pub trait Settings: 'static + Send + Sync {    /// The name of a key within the JSON file from which this setting should    /// be deserialized. If this is `None`, then the setting will be deserialized    /// from the root object.    const KEY: Option<&'static str>;     /// The type that is stored in an individual JSON file.    type FileContent: Clone + Default + Serialize + DeserializeOwned + JsonSchema;     /// The logic for combining together values from one or more JSON files into the    /// final value for this setting.    fn load(sources: SettingsSources<Self::FileContent>, cx: &mut AppContext) -> Result<Self>    where        Self: Sized;     fn json_schema(        generator: &mut SchemaGenerator,        _: &SettingsJsonSchemaParams,        _: &AppContext,    ) -> RootSchema {        generator.root_schema_for::<Self::FileContent>()    }     fn missing_default() -> anyhow::Error {        anyhow::anyhow!("missing default")    }     fn register(cx: &mut AppContext)    where        Self: Sized,    {        SettingsStore::update_global(cx, |store, cx| {            store.register_setting::<Self>(cx);        });    }     #[track_caller]    fn get<'a>(path: Option<SettingsLocation>, cx: &'a AppContext) -> &'a Self    where        Self: Sized,    {        cx.global::<SettingsStore>().get(path)    }     #[track_caller]    fn get_global(cx: &AppContext) -> &Self    where        Self: Sized,    {        cx.global::<SettingsStore>().get(None)    }     #[track_caller]    fn try_read_global<R>(cx: &AsyncAppContext, f: impl FnOnce(&Self) -> R) -> Option<R>    where        Self: Sized,    {        cx.try_read_global(|s: &SettingsStore, _| f(s.get(None)))    }     #[track_caller]    fn override_global(settings: Self, cx: &mut AppContext)    where        Self: Sized,    {        cx.global_mut::<SettingsStore>().override_global(settings)    }} #[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 provided customizations.    ///    /// Customizations later in the iterator win out over the earlier ones.    pub fn json_merge_with<O: DeserializeOwned>(        customizations: impl Iterator<Item = &'a T>,    ) -> Result<O> {        let mut merged = serde_json::Value::Null;        for value in customizations {            merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);        }        Ok(serde_json::from_value(merged)?)    }     /// 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>(&'a self) -> Result<O> {        Self::json_merge_with(self.defaults_and_customizations())    }} #[derive(Clone, Copy)]pub struct SettingsLocation<'a> {    pub worktree_id: usize,    pub path: &'a Path,} pub struct SettingsJsonSchemaParams<'a> {    pub staff_mode: bool,    pub language_names: &'a [String],    pub font_names: &'a [String],} /// A set of strongly-typed setting values defined via multiple JSON files.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,        Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,    )>,} impl Global for SettingsStore {} impl Default for SettingsStore {    fn default() -> Self {        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(),        }    }} #[derive(Debug)]struct SettingValue<T> {    global_value: Option<T>,    local_values: Vec<(usize, Arc<Path>, T)>,} trait AnySettingValue: 'static + Send + Sync {    fn key(&self) -> Option<&'static str>;    fn setting_type_name(&self) -> &'static str;    fn deserialize_setting(&self, json: &serde_json::Value) -> Result<DeserializedSetting>;    fn load_setting(        &self,        sources: SettingsSources<DeserializedSetting>,        cx: &mut AppContext,    ) -> Result<Box<dyn Any>>;    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;    fn set_global_value(&mut self, value: Box<dyn Any>);    fn set_local_value(&mut self, root_id: usize, path: Arc<Path>, value: Box<dyn Any>);    fn json_schema(        &self,        generator: &mut SchemaGenerator,        _: &SettingsJsonSchemaParams,        cx: &AppContext,    ) -> RootSchema;} struct DeserializedSetting(Box<dyn Any>); impl SettingsStore {    pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R    where        C: BorrowAppContext,    {        cx.update_global(f)    }     /// Add a new type of setting to the store.    pub fn register_setting<T: Settings>(&mut self, cx: &mut AppContext) {        let setting_type_id = TypeId::of::<T>();        let entry = self.setting_values.entry(setting_type_id);        if matches!(entry, hash_map::Entry::Occupied(_)) {            return;        }         let setting_value = entry.or_insert(Box::new(SettingValue::<T> {            global_value: None,            local_values: Vec::new(),        }));         if let Some(default_settings) = setting_value            .deserialize_setting(&self.raw_default_settings)            .log_err()        {            let user_value = setting_value                .deserialize_setting(&self.raw_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())            {                release_channel_value = setting_value                    .deserialize_setting(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(                    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()            {                setting_value.set_global_value(setting);            }        }    }     /// Get the value of a setting.    ///    /// Panics if the given setting type has not been registered, or if there is no    /// value for this setting.    pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T {        self.setting_values            .get(&TypeId::of::<T>())            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))            .value_for_path(path)            .downcast_ref::<T>()            .expect("no default value for setting type")    }     /// Override the global value for a setting.    ///    /// The given value will be overwritten if the user settings file changes.    pub fn override_global<T: Settings>(&mut self, value: T) {        self.setting_values            .get_mut(&TypeId::of::<T>())            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))            .set_global_value(Box::new(value))    }     /// Get the user's settings as a raw JSON value.    ///    /// This is only for debugging and reporting. For user-facing functionality,    /// use the typed setting interface.    pub fn raw_user_settings(&self) -> &serde_json::Value {        &self.raw_user_settings    }     #[cfg(any(test, feature = "test-support"))]    pub fn test(cx: &mut AppContext) -> Self {        let mut this = Self::default();        this.set_default_settings(&crate::test_settings(), cx)            .unwrap();        this.set_user_settings("{}", cx).unwrap();        this    }     /// Updates the value of a setting in the user's global configuration.    ///    /// This is only for tests. Normally, settings are only loaded from    /// JSON files.    #[cfg(any(test, feature = "test-support"))]    pub fn update_user_settings<T: Settings>(        &mut self,        cx: &mut AppContext,        update: impl FnOnce(&mut T::FileContent),    ) {        let old_text = serde_json::to_string(&self.raw_user_settings).unwrap();        let new_text = self.new_text_for_update::<T>(old_text, update);        self.set_user_settings(&new_text, cx).unwrap();    }     /// Updates the value of a setting in a JSON file, returning the new text    /// for that JSON file.    pub fn new_text_for_update<T: Settings>(        &self,        old_text: String,        update: impl FnOnce(&mut T::FileContent),    ) -> String {        let edits = self.edits_for_update::<T>(&old_text, update);        let mut new_text = old_text;        for (range, replacement) in edits.into_iter() {            new_text.replace_range(range, &replacement);        }        new_text    }     /// Updates the value of a setting in a JSON file, returning a list    /// of edits to apply to the JSON file.    pub fn edits_for_update<T: Settings>(        &self,        text: &str,        update: impl FnOnce(&mut T::FileContent),    ) -> Vec<(Range<usize>, String)> {        let setting_type_id = TypeId::of::<T>();         let setting = self            .setting_values            .get(&setting_type_id)            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()));        let raw_settings = parse_json_with_comments::<serde_json::Value>(text).unwrap_or_default();        let old_content = match setting.deserialize_setting(&raw_settings) {            Ok(content) => content.0.downcast::<T::FileContent>().unwrap(),            Err(_) => Box::<<T as Settings>::FileContent>::default(),        };        let mut new_content = old_content.clone();        update(&mut new_content);         let old_value = serde_json::to_value(&old_content).unwrap();        let new_value = serde_json::to_value(new_content).unwrap();         let mut key_path = Vec::new();        if let Some(key) = T::KEY {            key_path.push(key);        }         let mut edits = Vec::new();        let tab_size = self.json_tab_size();        let mut text = text.to_string();        update_value_in_json_text(            &mut text,            &mut key_path,            tab_size,            &old_value,            &new_value,            &mut edits,        );        edits    }     /// Configure the tab sized when updating JSON files.    pub fn set_json_tab_size_callback<T: Settings>(        &mut self,        get_tab_size: fn(&T) -> Option<usize>,    ) {        self.tab_size_callback = Some((            TypeId::of::<T>(),            Box::new(move |value| get_tab_size(value.downcast_ref::<T>().unwrap())),        ));    }     fn json_tab_size(&self) -> usize {        const DEFAULT_JSON_TAB_SIZE: usize = 2;         if let Some((setting_type_id, callback)) = &self.tab_size_callback {            let setting_value = self.setting_values.get(setting_type_id).unwrap();            let value = setting_value.value_for_path(None);            if let Some(value) = callback(value) {                return value;            }        }         DEFAULT_JSON_TAB_SIZE    }     /// Sets the default settings via a JSON string.    ///    /// The string should contain a JSON object with a default value for every setting.    pub fn set_default_settings(        &mut self,        default_settings_content: &str,        cx: &mut AppContext,    ) -> Result<()> {        let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?;        if settings.is_object() {            self.raw_default_settings = settings;            self.recompute_values(None, cx)?;            Ok(())        } else {            Err(anyhow!("settings must be an object"))        }    }     /// Sets the user settings via a JSON string.    pub fn set_user_settings(        &mut self,        user_settings_content: &str,        cx: &mut AppContext,    ) -> Result<()> {        let settings: serde_json::Value = if user_settings_content.is_empty() {            parse_json_with_comments("{}")?        } else {            parse_json_with_comments(user_settings_content)?        };        if settings.is_object() {            self.raw_user_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 set_local_settings(        &mut self,        root_id: usize,        path: Arc<Path>,        settings_content: Option<&str>,        cx: &mut AppContext,    ) -> Result<()> {        if settings_content.is_some_and(|content| !content.is_empty()) {            self.raw_local_settings.insert(                (root_id, path.clone()),                parse_json_with_comments(settings_content.unwrap())?,            );        } else {            self.raw_local_settings.remove(&(root_id, path.clone()));        }        self.recompute_values(Some((root_id, &path)), cx)?;        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);        self.recompute_values(Some((root_id, "".as_ref())), cx)?;        Ok(())    }     pub fn local_settings(&self, root_id: usize) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {        self.raw_local_settings            .range((root_id, Path::new("").into())..(root_id + 1, Path::new("").into()))            .map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap()))    }     pub fn json_schema(        &self,        schema_params: &SettingsJsonSchemaParams,        cx: &AppContext,    ) -> serde_json::Value {        use schemars::{

Lines 1–500 of 1456.