2267 lines · 76.1 KB · Rust
RawDownload
use anyhow::{Context as _, Result};use collections::{BTreeMap, HashMap, btree_map, hash_map};use ec4rs::{ConfigParser, PropertiesSource, Section};use fs::Fs;use futures::{FutureExt, StreamExt, channel::mpsc, future::LocalBoxFuture};use gpui::{App, AsyncApp, BorrowAppContext, Global, Task, UpdateGlobal}; use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name};use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::RootSchema};use serde::{Deserialize, Serialize, de::DeserializeOwned};use serde_json::Value;use smallvec::SmallVec;use std::{    any::{Any, TypeId, type_name},    fmt::Debug,    ops::Range,    path::{Path, PathBuf},    str::{self, FromStr},    sync::{Arc, LazyLock},};use streaming_iterator::StreamingIterator;use tree_sitter::Query;use util::RangeExt; use util::{ResultExt as _, merge_non_null_json_value_into}; pub type EditorconfigProperties = ec4rs::Properties; use crate::{SettingsJsonSchemaParams, VsCodeSettings, 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>;     const FALLBACK_KEY: Option<&'static str> = None;     /// The name of the keys in the [`FileContent`](Self::FileContent) that should    /// always be written to a settings file, even if their value matches the default    /// value.    ///    /// This is useful for tagged [`FileContent`](Self::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 App) -> Result<Self>    where        Self: Sized;     fn json_schema(        generator: &mut SchemaGenerator,        _: &SettingsJsonSchemaParams,        _: &App,    ) -> RootSchema {        generator.root_schema_for::<Self::FileContent>()    }     fn missing_default() -> anyhow::Error {        anyhow::anyhow!("missing default")    }     /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known    /// equivalent settings from a vscode config to our config    fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent);     #[track_caller]    fn register(cx: &mut App)    where        Self: Sized,    {        SettingsStore::update_global(cx, |store, cx| {            store.register_setting::<Self>(cx);        });    }     #[track_caller]    fn get<'a>(path: Option<SettingsLocation>, cx: &'a App) -> &'a Self    where        Self: Sized,    {        cx.global::<SettingsStore>().get(path)    }     #[track_caller]    fn get_global(cx: &App) -> &Self    where        Self: Sized,    {        cx.global::<SettingsStore>().get(None)    }     #[track_caller]    fn try_read_global<R>(cx: &AsyncApp, 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 App)    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,    /// Global settings (loaded before user settings).    pub global: Option<&'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.global            .into_iter()            .chain(self.extensions)            .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 = 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: Value,    raw_global_settings: Option<Value>,    raw_user_settings: Value,    raw_server_settings: Option<Value>,    raw_extension_settings: Value,    raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), 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(AsyncApp) -> 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,    Debug,} 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: &Value) -> Result<DeserializedSetting> {        self.deserialize_setting_with_key(json).1    }    fn deserialize_setting_with_key(        &self,        json: &Value,    ) -> (Option<&'static str>, Result<DeserializedSetting>);    fn load_setting(        &self,        sources: SettingsSources<DeserializedSetting>,        cx: &mut App,    ) -> Result<Box<dyn Any>>;    fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &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: &App,    ) -> RootSchema;    fn edits_for_update(        &self,        raw_settings: &serde_json::Value,        tab_size: usize,        vscode_settings: &VsCodeSettings,        text: &mut String,        edits: &mut Vec<(Range<usize>, String)>,    );} struct DeserializedSetting(Box<dyn Any>); impl SettingsStore {    pub fn new(cx: &App) -> 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_global_settings: None,            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(async move |cx| {                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 App) {        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,                        global: None,                        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")    }     /// Get all values from project specific settings    pub fn get_all_locals<T: Settings>(&self) -> Vec<(WorktreeId, Arc<Path>, &T)> {        self.setting_values            .get(&TypeId::of::<T>())            .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))            .all_local_values()            .into_iter()            .map(|(id, path, any)| {                (                    id,                    path,                    any.downcast_ref::<T>()                        .expect("wrong value type for setting"),                )            })            .collect()    }     /// 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) -> &Value {        &self.raw_user_settings    }     /// Access the raw JSON value of the global settings.    pub fn raw_global_settings(&self) -> Option<&Value> {        self.raw_global_settings.as_ref()    }     #[cfg(any(test, feature = "test-support"))]    pub fn test(cx: &mut App) -> 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 App,        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();    }     pub 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 async fn load_global_settings(fs: &Arc<dyn Fs>) -> Result<String> {        match fs.load(paths::global_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("{}".to_string());                    }                }                Err(err)            }        }    }     pub fn update_settings_file<T: Settings>(        &self,        fs: Arc<dyn Fs>,        update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),    ) {        self.setting_file_updates_tx            .unbounded_send(Box::new(move |cx: AsyncApp| {                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 settings_path = paths::settings_file().as_path();                    if fs.is_file(settings_path).await {                        let resolved_path =                            fs.canonicalize(settings_path).await.with_context(|| {                                format!("Failed to canonicalize settings path {:?}", settings_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(settings_path.to_path_buf(), new_text)                            .await                            .with_context(|| {

Lines 1–500 of 2267.