6796 lines · 250.2 KB · Rust
RawDownload
mod components;mod page_data;pub mod pages; use agent_skills::SkillIndex;use anyhow::{Context as _, Result};use cloud_api_types::OrganizationConfiguration;use editor::{Editor, EditorEvent};use futures::{StreamExt, channel::mpsc};use fuzzy::StringMatchCandidate;use gpui::{    Action, App, AsyncApp, ClipboardItem, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle,    Focusable, Global, KeyContext, ListState, ReadGlobal as _, Role, ScrollHandle, Stateful,    Subscription, Task, TitlebarOptions, UniformListScrollHandle, WeakEntity, Window, WindowBounds,    WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, uniform_list,}; use language::Buffer;use platform_title_bar::PlatformTitleBar;use project::{Project, ProjectPath, Worktree, WorktreeId};use release_channel::ReleaseChannel;use schemars::JsonSchema;use serde::Deserialize;use settings::{    IntoGpui, Settings, SettingsContent, SettingsStore, initial_project_settings_content,};use std::{    any::{Any, TypeId, type_name},    cell::RefCell,    collections::{HashMap, HashSet},    num::{NonZero, NonZeroU32},    ops::Range,    path::PathBuf,    rc::Rc,    sync::{Arc, LazyLock, RwLock},    time::Duration,};use theme_settings::ThemeSettings;use ui::{    Banner, ContextMenu, Divider, DropdownMenu, DropdownStyle, IconButtonShape, KeyBinding,    KeybindingHint, PopoverMenu, Scrollbars, Switch, Tooltip, TreeViewItem, WithScrollbar,    prelude::*,}; use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath};use workspace::{    AppState, MultiWorkspace, OpenOptions, OpenVisible, Workspace, WorkspaceSettings,    client_side_decorations,};use zed_actions::{    AGENT_SKILLS_SETTINGS_PATH, OpenProjectSettings, OpenSettings, OpenSettingsAt,    OpenSettingsAtTarget, OpenSettingsPage,}; use crate::components::{    EnumVariantDropdown, NumberField, NumberFieldMode, NumberFieldType, SettingsInputField,    SettingsSectionHeader, font_picker, icon_theme_picker, render_ollama_model_picker,    text_field_a11y_state, theme_picker,};use crate::pages::{    CustomAgentForm, LlmProviderForm, McpServerForm, render_input_audio_device_dropdown,    render_output_audio_device_dropdown,}; const NAVBAR_CONTAINER_TAB_INDEX: isize = 0;const NAVBAR_GROUP_TAB_INDEX: isize = 1; const HEADER_CONTAINER_TAB_INDEX: isize = 2;const HEADER_GROUP_TAB_INDEX: isize = 3; const CONTENT_CONTAINER_TAB_INDEX: isize = 4;const CONTENT_GROUP_TAB_INDEX: isize = 5; const SIDEBAR_WIDTH: Pixels = px(226.);const CONTENT_MIN_WIDTH: Pixels = px(400.); actions!(    settings_editor,    [        /// Minimizes the settings UI window.        Minimize,        /// Toggles focus between the navbar and the main content.        ToggleFocusNav,        /// Expands the navigation entry.        ExpandNavEntry,        /// Collapses the navigation entry.        CollapseNavEntry,        /// Focuses the next file in the file list.        FocusNextFile,        /// Focuses the previous file in the file list.        FocusPreviousFile,        /// Opens an editor for the current file        OpenCurrentFile,        /// Focuses the previous root navigation entry.        FocusPreviousRootNavEntry,        /// Focuses the next root navigation entry.        FocusNextRootNavEntry,        /// Focuses the first navigation entry.        FocusFirstNavEntry,        /// Focuses the last navigation entry.        FocusLastNavEntry,        /// Focuses and opens the next navigation entry without moving focus to content.        FocusNextNavEntry,        /// Focuses and opens the previous navigation entry without moving focus to content.        FocusPreviousNavEntry    ]); #[derive(Action, PartialEq, Eq, Clone, Copy, Debug, JsonSchema, Deserialize)]#[action(namespace = settings_editor)]struct FocusFile(pub u32); struct SettingField<T: 'static> {    pick: fn(&SettingsContent) -> Option<&T>,    write: fn(&mut SettingsContent, Option<T>, &App),    /// Tells us whether the setting is overridden by the currently selected    /// organization's settings. Takes the organization configuration and the    /// resolved settings value, and returns `Some(...)` if the organization    /// overrides the setting, otherwise `None`.    organization_override: Option<fn(&OrganizationConfiguration) -> Option<&T>>,     /// A json-path-like string that gives a unique-ish string that identifies    /// where in the JSON the setting is defined.    ///    /// The syntax is `jq`-like, but modified slightly to be URL-safe (and    /// without the leading dot), e.g. `foo.bar`.    ///    /// They are URL-safe (this is important since links are the main use-case    /// for these paths).    ///    /// There are a couple of special cases:    /// - discrimminants are represented with a trailing `$`, for example    /// `terminal.working_directory$`. This is to distinguish the discrimminant    /// setting (i.e. the setting that changes whether the value is a string or    /// an object) from the setting in the case that it is a string.    /// - language-specific settings begin `languages.$(language)`. Links    /// targeting these settings should take the form `languages/Rust/...`, for    /// example, but are not currently supported.    json_path: Option<&'static str>,} impl<T: 'static> Clone for SettingField<T> {    fn clone(&self) -> Self {        *self    }} // manual impl because derive puts a Copy bound on T, which is inaccurate in our caseimpl<T: 'static> Copy for SettingField<T> {} /// Helper for unimplemented settings, used in combination with `SettingField::unimplemented`/// to keep the setting around in the UI with valid pick and write implementations, but don't actually try to render it./// TODO(settings_ui): In non-dev builds (`#[cfg(not(debug_assertions))]`) make this render as edit-in-json#[derive(Clone, Copy)]struct UnimplementedSettingField; impl PartialEq for UnimplementedSettingField {    fn eq(&self, _other: &Self) -> bool {        true    }} impl<T: 'static> SettingField<T> {    /// Helper for settings with types that are not yet implemented.    #[allow(unused)]    fn unimplemented(self) -> SettingField<UnimplementedSettingField> {        SettingField {            pick: |_| Some(&UnimplementedSettingField),            write: |_, _, _| unreachable!(),            organization_override: None,            json_path: self.json_path,        }    }} trait AnySettingField {    fn as_any(&self) -> &dyn Any;    fn type_name(&self) -> &'static str;    fn type_id(&self) -> TypeId;    // Returns the file this value was set in and true, or File::Default and false to indicate it was not found in any file (missing default)    fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> (settings::SettingsFile, bool);    fn reset_to_default_fn(        &self,        current_file: &SettingsUiFile,        file_set_in: &settings::SettingsFile,        cx: &App,    ) -> Option<Box<dyn Fn(&mut Window, &mut App)>>;     fn json_path(&self) -> Option<&'static str>;     fn is_overridden_by_organization(&self, cx: &App) -> bool;} impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingField<T> {    fn as_any(&self) -> &dyn Any {        self    }     fn type_name(&self) -> &'static str {        type_name::<T>()    }     fn type_id(&self) -> TypeId {        TypeId::of::<T>()    }     fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> (settings::SettingsFile, bool) {        let (file, value) = cx            .global::<SettingsStore>()            .get_value_from_file(file.to_settings(), self.pick);        return (file, value.is_some());    }     fn reset_to_default_fn(        &self,        current_file: &SettingsUiFile,        file_set_in: &settings::SettingsFile,        cx: &App,    ) -> Option<Box<dyn Fn(&mut Window, &mut App)>> {        if file_set_in == &settings::SettingsFile::Default {            return None;        }        if file_set_in != &current_file.to_settings() {            return None;        }        let this = *self;        let store = SettingsStore::global(cx);        let default_value = (this.pick)(store.raw_default_settings());        let is_default = store            .get_content_for_file(file_set_in.clone())            .map_or(None, this.pick)            == default_value;        if is_default {            return None;        }        let current_file = current_file.clone();         return Some(Box::new(move |window, cx| {            let store = SettingsStore::global(cx);            let default_value = (this.pick)(store.raw_default_settings());            let is_set_somewhere_other_than_default = store                .get_value_up_to_file(current_file.to_settings(), this.pick)                .0                != settings::SettingsFile::Default;            let value_to_set = if is_set_somewhere_other_than_default {                default_value.cloned()            } else {                None            };            update_settings_file(                current_file.clone(),                None,                window,                cx,                move |settings, app| {                    (this.write)(settings, value_to_set, app);                },            )            // todo(settings_ui): Don't log err            .log_err();        }));    }     fn json_path(&self) -> Option<&'static str> {        self.json_path    }     fn is_overridden_by_organization(&self, cx: &App) -> bool {        let Some(org_override) = self.organization_override else {            return false;        };         let user_store = AppState::global(cx).user_store.read(cx);        let Some(org_config) = user_store.current_organization_configuration() else {            return false;        };         (org_override)(&org_config).is_some()    }} #[derive(Default, Clone)]struct SettingFieldRenderer {    renderers: Rc<        RefCell<            HashMap<                TypeId,                Box<                    dyn Fn(                        &SettingsWindow,                        &SettingItem,                        SettingsUiFile,                        Option<&SettingsFieldMetadata>,                        bool,                        &mut Window,                        &mut Context<SettingsWindow>,                    ) -> Stateful<Div>,                >,            >,        >,    >,} impl Global for SettingFieldRenderer {} impl SettingFieldRenderer {    fn add_basic_renderer<T: 'static>(        &mut self,        render_control: impl Fn(            SettingField<T>,            SettingsUiFile,            Option<&SettingsFieldMetadata>,            &'static str,            &'static str,            &mut Window,            &mut App,        ) -> AnyElement        + 'static,    ) -> &mut Self {        self.add_renderer(            move |settings_window: &SettingsWindow,                  item: &SettingItem,                  field: SettingField<T>,                  settings_file: SettingsUiFile,                  metadata: Option<&SettingsFieldMetadata>,                  sub_field: bool,                  window: &mut Window,                  cx: &mut Context<SettingsWindow>| {                let control = render_control(                    field,                    settings_file.clone(),                    metadata,                    item.title,                    item.description,                    window,                    cx,                );                render_settings_item(settings_window, item, settings_file, control, sub_field, cx)            },        )    }     fn add_renderer<T: 'static>(        &mut self,        renderer: impl Fn(            &SettingsWindow,            &SettingItem,            SettingField<T>,            SettingsUiFile,            Option<&SettingsFieldMetadata>,            bool,            &mut Window,            &mut Context<SettingsWindow>,        ) -> Stateful<Div>        + 'static,    ) -> &mut Self {        let key = TypeId::of::<T>();        let renderer = Box::new(            move |settings_window: &SettingsWindow,                  item: &SettingItem,                  settings_file: SettingsUiFile,                  metadata: Option<&SettingsFieldMetadata>,                  sub_field: bool,                  window: &mut Window,                  cx: &mut Context<SettingsWindow>| {                let field = *item                    .field                    .as_ref()                    .as_any()                    .downcast_ref::<SettingField<T>>()                    .unwrap();                renderer(                    settings_window,                    item,                    field,                    settings_file,                    metadata,                    sub_field,                    window,                    cx,                )            },        );        self.renderers.borrow_mut().insert(key, renderer);        self    }} struct NonFocusableHandle {    handle: FocusHandle,    _subscription: Subscription,} impl NonFocusableHandle {    fn new(tab_index: isize, tab_stop: bool, window: &mut Window, cx: &mut App) -> Entity<Self> {        let handle = cx.focus_handle().tab_index(tab_index).tab_stop(tab_stop);        Self::from_handle(handle, window, cx)    }     fn from_handle(handle: FocusHandle, window: &mut Window, cx: &mut App) -> Entity<Self> {        cx.new(|cx| {            let _subscription = cx.on_focus(&handle, window, {                move |_, window, cx| {                    window.focus_next(cx);                }            });            Self {                handle,                _subscription,            }        })    }} impl Focusable for NonFocusableHandle {    fn focus_handle(&self, _: &App) -> FocusHandle {        self.handle.clone()    }} #[derive(Default)]struct SettingsFieldMetadata {    placeholder: Option<&'static str>,    should_do_titlecase: Option<bool>,    display_confirm_button: bool,    display_clear_button: bool,    confirm_on_focus_out: bool,    treat_missing_text_as_empty: bool,} pub fn init(cx: &mut App) {    init_renderers(cx);    let queue = ProjectSettingsUpdateQueue::new(cx);    cx.set_global(queue);     cx.on_action(|_: &OpenSettings, cx| {        open_settings_editor(None, None, None, cx);    });    cx.on_action(|_: &zed_actions::assistant::OpenSkillCreator, cx| {        open_skill_creator(pages::SkillCreatorOpenMode::Form, None, cx);    });    cx.on_action(|_: &zed_actions::assistant::CreateSkillFromUrl, cx| {        let initial_url = pages::skill_url_from_clipboard(cx);        open_skill_creator(pages::SkillCreatorOpenMode::Url { initial_url }, None, cx);    });     cx.observe_new(|workspace: &mut workspace::Workspace, _, _| {        workspace            .register_action(|_, action: &OpenSettingsAt, window, cx| {                let window_handle = window.window_handle().downcast::<MultiWorkspace>();                open_settings_editor_at_target(                    Some(&action.path),                    action.target.as_ref().map(SettingsFileTarget::from),                    window_handle,                    cx,                );            })            .register_action(|_, action: &OpenSettingsPage, window, cx| {                let window_handle = window.window_handle().downcast::<MultiWorkspace>();                open_settings_editor_to_page(                    &action.page,                    action.target.as_ref().map(SettingsFileTarget::from),                    window_handle,                    cx,                );            })            .register_action(|_, _: &OpenSettings, window, cx| {                let window_handle = window.window_handle().downcast::<MultiWorkspace>();                open_settings_editor(None, None, window_handle, cx);            })            .register_action(|workspace, _: &OpenProjectSettings, window, cx| {                let window_handle = window.window_handle().downcast::<MultiWorkspace>();                let target_worktree_id = workspace                    .project()                    .read(cx)                    .visible_worktrees(cx)                    .find_map(|tree| {                        tree.read(cx)                            .root_entry()?                            .is_dir()                            .then_some(tree.read(cx).id())                    });                open_settings_editor(None, target_worktree_id, window_handle, cx);            })            .register_action(                |_, _: &zed_actions::assistant::OpenSkillCreator, window, cx| {                    let window_handle = window.window_handle().downcast::<MultiWorkspace>();                    open_skill_creator(pages::SkillCreatorOpenMode::Form, window_handle, cx);                },            )            .register_action(                |_, _: &zed_actions::assistant::CreateSkillFromUrl, window, cx| {                    let window_handle = window.window_handle().downcast::<MultiWorkspace>();                    let initial_url = pages::skill_url_from_clipboard(cx);                    open_skill_creator(                        pages::SkillCreatorOpenMode::Url { initial_url },                        window_handle,                        cx,                    );                },

Lines 1–500 of 6796.