3269 lines · 108.7 KB · Rust
RawDownload
use anyhow::{Context as _, Result};use collections::{BTreeMap, HashMap, TypeIdHashMap, btree_map, hash_map};use fs::Fs;use futures::{    FutureExt, StreamExt,    channel::{mpsc, oneshot},    future::LocalBoxFuture,};use gpui::{    App, AppContext, AsyncApp, BorrowAppContext, Entity, Global, SharedString, Task, UpdateGlobal,}; use paths::{local_settings_file_relative_path, task_file_name};use schemars::{JsonSchema, json_schema};use serde_json::Value;use settings_content::{CommandAliasTarget, ParseStatus};use std::{    any::{Any, TypeId, type_name},    fmt::Debug,    ops::Range,    path::{Path, PathBuf},    rc::Rc,    str,    sync::Arc,};use util::{    ResultExt as _,    rel_path::RelPath,    schemars::{AllowTrailingCommas, DefaultDenyUnknownFields, replace_subschema},}; use crate::editorconfig_store::EditorconfigStore; use crate::{    ActiveSettingsProfileName, FileTypeMap, FontFamilyName, IconThemeName, LanguageSettingsContent,    LanguageToSettingsMap, LspSettings, LspSettingsMap, SemanticTokenRules, ThemeName,    UserSettingsContentExt, VsCodeSettings, WorktreeId,    settings_content::{        ExtendingSet, ExtensionsSettingsContent, ProfileBase, ProjectSettingsContent,        RootUserSettings, SettingsContent, UserSettingsContent, merge_from::MergeFrom,    },}; use settings_json::{infer_json_indent_size, update_value_in_json_text}; pub const LSP_SETTINGS_SCHEMA_URL_PREFIX: &str = "zed://schemas/settings/lsp/"; pub trait SettingsKey: '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;} /// 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 + Sized {    /// The name of the keys in the [`SettingsContent`] that should    /// always be written to a settings file, even if their value matches the default    /// value.    ///    /// This is useful for tagged [`SettingsContent`]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;     /// Read the value from default.json.    ///    /// This function *should* panic if default values are missing,    /// and you should add a default to default.json for documentation.    fn from_settings(content: &SettingsContent) -> Self;     #[track_caller]    fn register(cx: &mut App)    where        Self: Sized,    {        SettingsStore::update_global(cx, |store, _| {            store.register_setting::<Self>();        });    }     #[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_get(cx: &App) -> Option<&Self>    where        Self: Sized,    {        if cx.has_global::<SettingsStore>() {            cx.global::<SettingsStore>().try_get(None)        } else {            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)    }} pub struct RegisteredSetting {    pub settings_value: fn() -> Box<dyn AnySettingValue>,    pub from_settings: fn(&SettingsContent) -> Box<dyn Any>,    pub id: fn() -> TypeId,} inventory::collect!(RegisteredSetting); #[derive(Clone, Copy, Debug)]pub struct SettingsLocation<'a> {    pub worktree_id: WorktreeId,    pub path: &'a RelPath,} pub struct SettingsStore {    setting_values: TypeIdHashMap<Box<dyn AnySettingValue>>,    default_settings: Rc<SettingsContent>,    user_settings: Option<UserSettingsContent>,    global_settings: Option<Box<SettingsContent>>,     extension_settings: Option<Box<SettingsContent>>,    server_settings: Option<Box<SettingsContent>>,     language_semantic_token_rules: HashMap<SharedString, SemanticTokenRules>,     merged_settings: Rc<SettingsContent>,     last_user_settings_content: Option<String>,    last_global_settings_content: Option<String>,    local_settings: BTreeMap<(WorktreeId, Arc<RelPath>), SettingsContent>,    pub editorconfig_store: Entity<EditorconfigStore>,     _settings_files_watcher: Option<Task<()>>,    _setting_file_updates: Task<()>,    setting_file_updates_tx:        mpsc::UnboundedSender<Box<dyn FnOnce(AsyncApp) -> LocalBoxFuture<'static, Result<()>>>>,    file_errors: BTreeMap<SettingsFile, SettingsParseResult>,} #[derive(Clone, PartialEq, Eq, Debug)]pub enum SettingsFile {    Default,    Global,    User,    Server,    /// Represents project settings in ssh projects as well as local projects    Project((WorktreeId, Arc<RelPath>)),} impl PartialOrd for SettingsFile {    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {        Some(self.cmp(other))    }} /// Sorted in order of precedenceimpl Ord for SettingsFile {    fn cmp(&self, other: &Self) -> std::cmp::Ordering {        use SettingsFile::*;        use std::cmp::Ordering;        match (self, other) {            (User, User) => Ordering::Equal,            (Server, Server) => Ordering::Equal,            (Default, Default) => Ordering::Equal,            (Project((id1, rel_path1)), Project((id2, rel_path2))) => id1                .cmp(id2)                .then_with(|| rel_path1.cmp(rel_path2).reverse()),            (Project(_), _) => Ordering::Less,            (_, Project(_)) => Ordering::Greater,            (Server, _) => Ordering::Less,            (_, Server) => Ordering::Greater,            (User, _) => Ordering::Less,            (_, User) => Ordering::Greater,            (Global, _) => Ordering::Less,            (_, Global) => Ordering::Greater,        }    }} #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]pub enum LocalSettingsKind {    Settings,    Tasks,    Editorconfig,    Debug,} #[derive(Clone, Debug, PartialEq, Eq, Hash)]pub enum LocalSettingsPath {    InWorktree(Arc<RelPath>),    OutsideWorktree(Arc<Path>),} impl LocalSettingsPath {    pub fn is_outside_worktree(&self) -> bool {        matches!(self, Self::OutsideWorktree(_))    }     pub fn to_proto(&self) -> String {        match self {            Self::InWorktree(path) => path.as_unix_str().to_owned(),            Self::OutsideWorktree(path) => path.to_string_lossy().to_string(),        }    }     pub fn from_proto(path: &str, is_outside_worktree: bool) -> anyhow::Result<Self> {        if is_outside_worktree {            Ok(Self::OutsideWorktree(PathBuf::from(path).into()))        } else {            Ok(Self::InWorktree(RelPath::from_unix_str(path)?.into()))        }    }} impl Global for SettingsStore {} #[derive(Default)]pub struct DefaultSemanticTokenRules(pub SemanticTokenRules); impl gpui::Global for DefaultSemanticTokenRules {} #[doc(hidden)]#[derive(Debug)]pub struct SettingValue<T> {    #[doc(hidden)]    pub global_value: Option<T>,    #[doc(hidden)]    pub local_values: Vec<(WorktreeId, Arc<RelPath>, T)>,} #[doc(hidden)]pub trait AnySettingValue: 'static + Send + Sync {    fn setting_type_name(&self) -> &'static str;     fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any>;     fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;    fn all_local_values(&self) -> Vec<(WorktreeId, Arc<RelPath>, &dyn Any)>;    fn set_global_value(&mut self, value: Box<dyn Any>);    fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<RelPath>, value: Box<dyn Any>);    fn clear_local_values(&mut self, root_id: WorktreeId);} /// Parameters that are used when generating some JSON schemas at runtime.pub struct SettingsJsonSchemaParams<'a> {    pub language_names: &'a [String],    pub font_names: &'a [String],    pub theme_names: &'a [SharedString],    pub icon_theme_names: &'a [SharedString],    pub lsp_adapter_names: &'a [String],    pub action_names: &'a [&'a str],    pub action_documentation: &'a HashMap<&'a str, &'a str>,    pub deprecations: &'a HashMap<&'a str, &'a str>,    pub deprecation_messages: &'a HashMap<&'a str, &'a str>,} impl SettingsStore {    pub fn new(cx: &mut App, default_settings: &str) -> Self {        Self::new_with_semantic_tokens(cx, default_settings)    }     pub fn new_with_semantic_tokens(cx: &mut App, default_settings: &str) -> Self {        let default_settings = Self::parse_default_settings(default_settings).unwrap();        Self::from_settings_content(cx, default_settings)    }     fn from_settings_content(cx: &mut App, default_settings: SettingsContent) -> Self {        let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();        if !cx.has_global::<DefaultSemanticTokenRules>() {            cx.set_global::<DefaultSemanticTokenRules>(                crate::parse_json_with_comments::<SemanticTokenRules>(                    &crate::default_semantic_token_rules(),                )                .map(DefaultSemanticTokenRules)                .unwrap_or_default(),            );        }        let default_settings: Rc<SettingsContent> = default_settings.into();        let mut this = Self {            setting_values: Default::default(),            default_settings: default_settings.clone(),            global_settings: None,            server_settings: None,            user_settings: None,            extension_settings: None,            language_semantic_token_rules: HashMap::default(),             merged_settings: default_settings,            last_user_settings_content: None,            last_global_settings_content: None,            local_settings: BTreeMap::default(),            editorconfig_store: cx.new(|_| EditorconfigStore::default()),            _settings_files_watcher: None,            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();                }            }),            file_errors: BTreeMap::default(),        };         this.load_settings_types();         this    }     pub fn observe_active_settings_profile_name(cx: &mut App) -> gpui::Subscription {        cx.observe_global::<ActiveSettingsProfileName>(|cx| {            Self::update_global(cx, |store, cx| {                store.recompute_values(None, cx);            });        })    }     pub fn update<C, R>(cx: &mut C, f: impl FnOnce(&mut Self, &mut C) -> R) -> R    where        C: BorrowAppContext,    {        cx.update_global(f)    }     pub fn watch_settings_files(        &mut self,        fs: Arc<dyn Fs>,        cx: &mut App,        settings_changed: impl 'static + Fn(SettingsFile, SettingsParseResult, &mut App),    ) {        let (mut user_settings_file_rx, user_settings_watcher) = crate::watch_config_file(            cx.background_executor(),            fs.clone(),            paths::settings_file().clone(),        );        let (mut global_settings_file_rx, global_settings_watcher) = crate::watch_config_file(            cx.background_executor(),            fs,            paths::global_settings_file().clone(),        );         let global_content = cx            .foreground_executor()            .block_on(global_settings_file_rx.next())            .unwrap();        let user_content = cx            .foreground_executor()            .block_on(user_settings_file_rx.next())            .unwrap();         let result = self.set_user_settings(&user_content, cx);        settings_changed(SettingsFile::User, result, cx);        let result = self.set_global_settings(&global_content, cx);        settings_changed(SettingsFile::Global, result, cx);         self._settings_files_watcher = Some(cx.spawn(async move |cx| {            let _user_settings_watcher = user_settings_watcher;            let _global_settings_watcher = global_settings_watcher;            let mut settings_streams = futures::stream::select(                global_settings_file_rx.map(|content| (SettingsFile::Global, content)),                user_settings_file_rx.map(|content| (SettingsFile::User, content)),            );             while let Some((settings_file, content)) = settings_streams.next().await {                cx.update_global(|store: &mut SettingsStore, cx| {                    let result = match settings_file {                        SettingsFile::User => store.set_user_settings(&content, cx),                        SettingsFile::Global => store.set_global_settings(&content, cx),                        _ => return,                    };                    settings_changed(settings_file, result, cx);                    cx.refresh_windows();                });            }        }));    }     /// Add a new type of setting to the store.    pub fn register_setting<T: Settings>(&mut self) {        self.register_setting_internal(&RegisteredSetting {            settings_value: || {                Box::new(SettingValue::<T> {                    global_value: None,                    local_values: Vec::new(),                })            },            from_settings: |content| Box::new(T::from_settings(content)),            id: || TypeId::of::<T>(),        });    }     fn load_settings_types(&mut self) {        for registered_setting in inventory::iter::<RegisteredSetting>() {            self.register_setting_internal(registered_setting);        }    }     fn register_setting_internal(&mut self, registered_setting: &RegisteredSetting) {        let entry = self.setting_values.entry((registered_setting.id)());         if matches!(entry, hash_map::Entry::Occupied(_)) {            return;        }         let setting_value = entry.or_insert((registered_setting.settings_value)());        let value = (registered_setting.from_settings)(&self.merged_settings);        setting_value.set_global_value(value);    }     pub fn merged_settings(&self) -> &SettingsContent {        &self.merged_settings    }     /// 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 the value of a setting.    ///    /// Does not panic    pub fn try_get<T: Settings>(&self, path: Option<SettingsLocation>) -> Option<&T> {        self.setting_values            .get(&TypeId::of::<T>())            .map(|value| value.value_for_path(path))            .and_then(|value| value.downcast_ref::<T>())    }     /// Get all values from project specific settings    pub fn get_all_locals<T: Settings>(&self) -> Vec<(WorktreeId, Arc<RelPath>, &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 content.    ///    /// For user-facing functionality use the typed setting interface.    /// (e.g. ProjectSettings::get_global(cx))    pub fn raw_user_settings(&self) -> Option<&UserSettingsContent> {        self.user_settings.as_ref()    } 

Lines 1–500 of 3269.