1774 lines · 60.3 KB · Rust
RawDownload
use anyhow::{anyhow, Context, Result};use collections::{btree_map, hash_map, BTreeMap, HashMap};use ec4rs::{ConfigParser, PropertiesSource, Section};use fs::Fs;use futures::{channel::mpsc, future::LocalBoxFuture, FutureExt, StreamExt};use gpui::{AppContext, AsyncAppContext, BorrowAppContext, Global, Task, UpdateGlobal};use paths::{local_settings_file_relative_path, EDITORCONFIG_NAME};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, PathBuf},    str::{self, FromStr},    sync::{Arc, LazyLock},};use tree_sitter::Query;use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _}; pub type EditorconfigProperties = ec4rs::Properties; use crate::{SettingsJsonSchemaParams, WorktreeId}; /// 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 name of the keys in the [`FileContent`] that should always be written to    /// a settings file, even if their value matches the default value.    ///    /// This is useful for tagged [`FileContent`]s where the tag is a "version" field    /// that should always be persisted, even if the current user settings match the    /// current version of the settings.    const PRESERVED_KEYS: Option<&'static [&'static str]> = None;     /// 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")    }     #[track_caller]    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 server's settings.    pub server: 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.server)            .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, Debug)]pub struct SettingsLocation<'a> {    pub worktree_id: WorktreeId,    pub path: &'a Path,} /// A set of strongly-typed setting values defined via multiple config files.pub struct SettingsStore {    setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,    raw_default_settings: serde_json::Value,    raw_user_settings: serde_json::Value,    raw_server_settings: Option<serde_json::Value>,    raw_extension_settings: serde_json::Value,    raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), serde_json::Value>,    raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>,    tab_size_callback: Option<(        TypeId,        Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,    )>,    _setting_file_updates: Task<()>,    setting_file_updates_tx: mpsc::UnboundedSender<        Box<dyn FnOnce(AsyncAppContext) -> LocalBoxFuture<'static, Result<()>>>,    >,} #[derive(Clone)]pub struct Editorconfig {    pub is_root: bool,    pub sections: SmallVec<[Section; 5]>,} impl FromStr for Editorconfig {    type Err = anyhow::Error;     fn from_str(contents: &str) -> Result<Self, Self::Err> {        let parser = ConfigParser::new_buffered(contents.as_bytes())            .context("creating editorconfig parser")?;        let is_root = parser.is_root;        let sections = parser            .collect::<Result<SmallVec<_>, _>>()            .context("parsing editorconfig sections")?;        Ok(Self { is_root, sections })    }} #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]pub enum LocalSettingsKind {    Settings,    Tasks,    Editorconfig,} impl Global for SettingsStore {} #[derive(Debug)]struct SettingValue<T> {    global_value: Option<T>,    local_values: Vec<(WorktreeId, 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: WorktreeId, 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 new(cx: &AppContext) -> Self {        let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();        Self {            setting_values: Default::default(),            raw_default_settings: serde_json::json!({}),            raw_user_settings: serde_json::json!({}),            raw_server_settings: None,            raw_extension_settings: serde_json::json!({}),            raw_local_settings: Default::default(),            raw_editorconfig_settings: BTreeMap::default(),            tab_size_callback: Default::default(),            setting_file_updates_tx,            _setting_file_updates: cx.spawn(|cx| async move {                while let Some(setting_file_update) = setting_file_updates_rx.next().await {                    (setting_file_update)(cx.clone()).await.log_err();                }            }),        }    }     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 server_value = self                .raw_server_settings                .as_ref()                .and_then(|server_setting| {                    setting_value.deserialize_setting(server_setting).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,                        extensions: extension_value.as_ref(),                        user: user_value.as_ref(),                        release_channel: release_channel_value.as_ref(),                        server: server_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.    ///    /// For user-facing functionality use the typed setting interface.    /// (e.g. ProjectSettings::get_global(cx))    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::new(cx);        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();    }     async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> {        match fs.load(paths::settings_file()).await {            result @ Ok(_) => result,            Err(err) => {                if let Some(e) = err.downcast_ref::<std::io::Error>() {                    if e.kind() == std::io::ErrorKind::NotFound {                        return Ok(crate::initial_user_settings_content().to_string());                    }                }                Err(err)            }        }    }     pub fn update_settings_file<T: Settings>(        &self,        fs: Arc<dyn Fs>,        update: impl 'static + Send + FnOnce(&mut T::FileContent, &AppContext),    ) {        self.setting_file_updates_tx            .unbounded_send(Box::new(move |cx: AsyncAppContext| {                async move {                    let old_text = Self::load_settings(&fs).await?;                    let new_text = cx.read_global(|store: &SettingsStore, cx| {                        store.new_text_for_update::<T>(old_text, |content| update(content, cx))                    })?;                    let initial_path = paths::settings_file().as_path();                    if fs.is_file(initial_path).await {                        let resolved_path =                            fs.canonicalize(initial_path).await.with_context(|| {                                format!("Failed to canonicalize settings path {:?}", initial_path)                            })?;                         fs.atomic_write(resolved_path.clone(), new_text)                            .await                            .with_context(|| {                                format!("Failed to write settings to file {:?}", resolved_path)                            })?;                    } else {                        fs.atomic_write(initial_path.to_path_buf(), new_text)                            .await                            .with_context(|| {                                format!("Failed to write settings to file {:?}", initial_path)                            })?;                    }                     anyhow::Ok(())                }                .boxed_local()            }))            .ok();    }     /// 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 preserved_keys = T::PRESERVED_KEYS.unwrap_or_default();         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,            preserved_keys,            &mut edits,        );        edits

Lines 1–500 of 1774.