5414 lines · 197.2 KB · Rust
RawDownload
mod components;mod page_data;pub mod pages; use anyhow::{Context as _, Result};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 _, ScrollHandle, Stateful,    Subscription, Task, Tiling, 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,    rc::Rc,    sync::{Arc, LazyLock, RwLock},    time::Duration,};use theme::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, client_side_decorations,};use zed_actions::{OpenProjectSettings, OpenSettings, OpenSettingsAt}; use crate::components::{    EnumVariantDropdown, NumberField, NumberFieldMode, NumberFieldType, SettingsInputField,    SettingsSectionHeader, font_picker, icon_theme_picker, render_ollama_model_picker,    theme_picker,};use crate::pages::{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; 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>),     /// 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!(),            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>;} 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, _| {                    (this.write)(settings, value_to_set);                },            )            // todo(settings_ui): Don't log err            .log_err();        }));    }     fn json_path(&self) -> Option<&'static str> {        self.json_path    }} #[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>,            &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>| {                render_settings_item(                    settings_window,                    item,                    settings_file.clone(),                    render_control(field, settings_file, metadata, window, cx),                    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>,} pub fn init(cx: &mut App) {    init_renderers(cx);    let queue = ProjectSettingsUpdateQueue::new(cx);    cx.set_global(queue);     cx.observe_new(|workspace: &mut workspace::Workspace, _, _| {        workspace            .register_action(                |workspace, OpenSettingsAt { path }: &OpenSettingsAt, window, cx| {                    let window_handle = window                        .window_handle()                        .downcast::<MultiWorkspace>()                        .expect("Workspaces are root Windows");                    open_settings_editor(workspace, Some(&path), None, window_handle, cx);                },            )            .register_action(|workspace, _: &OpenSettings, window, cx| {                let window_handle = window                    .window_handle()                    .downcast::<MultiWorkspace>()                    .expect("Workspaces are root Windows");                open_settings_editor(workspace, None, None, window_handle, cx);            })            .register_action(|workspace, _: &OpenProjectSettings, window, cx| {                let window_handle = window                    .window_handle()                    .downcast::<MultiWorkspace>()                    .expect("Workspaces are root Windows");                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(workspace, None, target_worktree_id, window_handle, cx);            });    })    .detach();} fn init_renderers(cx: &mut App) {    cx.default_global::<SettingFieldRenderer>()        .add_renderer::<UnimplementedSettingField>(            |settings_window, item, _, settings_file, _, sub_field, _, cx| {                render_settings_item(                    settings_window,                    item,                    settings_file,                    Button::new("open-in-settings-file", "Edit in settings.json")                        .style(ButtonStyle::Outlined)                        .size(ButtonSize::Medium)                        .tab_index(0_isize)                        .tooltip(Tooltip::for_action_title_in(                            "Edit in settings.json",                            &OpenCurrentFile,                            &settings_window.focus_handle,                        ))                        .on_click(cx.listener(|this, _, window, cx| {                            this.open_current_settings_file(window, cx);                        }))                        .into_any_element(),                    sub_field,                    cx,                )            },        )        .add_basic_renderer::<bool>(render_toggle_button)        .add_basic_renderer::<String>(render_text_field)        .add_basic_renderer::<SharedString>(render_text_field)        .add_basic_renderer::<settings::SaturatingBool>(render_toggle_button)        .add_basic_renderer::<settings::CursorShape>(render_dropdown)        .add_basic_renderer::<settings::RestoreOnStartupBehavior>(render_dropdown)        .add_basic_renderer::<settings::BottomDockLayout>(render_dropdown)        .add_basic_renderer::<settings::OnLastWindowClosed>(render_dropdown)        .add_basic_renderer::<settings::CloseWindowWhenNoItems>(render_dropdown)        .add_basic_renderer::<settings::TextRenderingMode>(render_dropdown)        .add_basic_renderer::<settings::FontFamilyName>(render_font_picker)        .add_basic_renderer::<settings::BaseKeymapContent>(render_dropdown)        .add_basic_renderer::<settings::MultiCursorModifier>(render_dropdown)        .add_basic_renderer::<settings::HideMouseMode>(render_dropdown)        .add_basic_renderer::<settings::CurrentLineHighlight>(render_dropdown)        .add_basic_renderer::<settings::ShowWhitespaceSetting>(render_dropdown)        .add_basic_renderer::<settings::SoftWrap>(render_dropdown)        .add_basic_renderer::<settings::ScrollBeyondLastLine>(render_dropdown)        .add_basic_renderer::<settings::SnippetSortOrder>(render_dropdown)        .add_basic_renderer::<settings::ClosePosition>(render_dropdown)        .add_basic_renderer::<settings::DockSide>(render_dropdown)        .add_basic_renderer::<settings::TerminalDockPosition>(render_dropdown)        .add_basic_renderer::<settings::DockPosition>(render_dropdown)        .add_basic_renderer::<settings::GitGutterSetting>(render_dropdown)        .add_basic_renderer::<settings::GitHunkStyleSetting>(render_dropdown)        .add_basic_renderer::<settings::GitPathStyle>(render_dropdown)        .add_basic_renderer::<settings::DiagnosticSeverityContent>(render_dropdown)        .add_basic_renderer::<settings::SeedQuerySetting>(render_dropdown)        .add_basic_renderer::<settings::DoubleClickInMultibuffer>(render_dropdown)        .add_basic_renderer::<settings::GoToDefinitionFallback>(render_dropdown)        .add_basic_renderer::<settings::ActivateOnClose>(render_dropdown)        .add_basic_renderer::<settings::ShowDiagnostics>(render_dropdown)        .add_basic_renderer::<settings::ShowCloseButton>(render_dropdown)        .add_basic_renderer::<settings::ProjectPanelEntrySpacing>(render_dropdown)        .add_basic_renderer::<settings::ProjectPanelSortMode>(render_dropdown)        .add_basic_renderer::<settings::RewrapBehavior>(render_dropdown)        .add_basic_renderer::<settings::FormatOnSave>(render_dropdown)        .add_basic_renderer::<settings::IndentGuideColoring>(render_dropdown)        .add_basic_renderer::<settings::IndentGuideBackgroundColoring>(render_dropdown)        .add_basic_renderer::<settings::FileFinderWidthContent>(render_dropdown)        .add_basic_renderer::<settings::ShowDiagnostics>(render_dropdown)

Lines 1–500 of 5414.