page_data.rs

   1use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt as _};
   2use gpui::{Action as _, App};
   3use itertools::Itertools as _;
   4use settings::{
   5    AudioInputDeviceName, AudioOutputDeviceName, LanguageSettingsContent, SemanticTokens,
   6    SettingsContent,
   7};
   8use std::sync::{Arc, OnceLock};
   9use strum::{EnumMessage, IntoDiscriminant as _, VariantArray};
  10use ui::IntoElement;
  11
  12use crate::{
  13    ActionLink, DynamicItem, PROJECT, SettingField, SettingItem, SettingsFieldMetadata,
  14    SettingsPage, SettingsPageItem, SubPageLink, USER, active_language, all_language_names,
  15    pages::{
  16        open_audio_test_window, render_edit_prediction_setup_page,
  17        render_tool_permissions_setup_page,
  18    },
  19};
  20
  21const DEFAULT_STRING: String = String::new();
  22/// A default empty string reference. Useful in `pick` functions for cases either in dynamic item fields, or when dealing with `settings::Maybe`
  23/// to avoid the "NO DEFAULT" case.
  24const DEFAULT_EMPTY_STRING: Option<&String> = Some(&DEFAULT_STRING);
  25
  26const DEFAULT_AUDIO_OUTPUT: AudioOutputDeviceName = AudioOutputDeviceName(None);
  27const DEFAULT_EMPTY_AUDIO_OUTPUT: Option<&AudioOutputDeviceName> = Some(&DEFAULT_AUDIO_OUTPUT);
  28const DEFAULT_AUDIO_INPUT: AudioInputDeviceName = AudioInputDeviceName(None);
  29const DEFAULT_EMPTY_AUDIO_INPUT: Option<&AudioInputDeviceName> = Some(&DEFAULT_AUDIO_INPUT);
  30
  31macro_rules! concat_sections {
  32    (@vec, $($arr:expr),+ $(,)?) => {{
  33        let total_len = 0_usize $(+ $arr.len())+;
  34        let mut out = Vec::with_capacity(total_len);
  35
  36        $(
  37            out.extend($arr);
  38        )+
  39
  40        out
  41    }};
  42
  43    ($($arr:expr),+ $(,)?) => {{
  44        let total_len = 0_usize $(+ $arr.len())+;
  45
  46        let mut out: Box<[std::mem::MaybeUninit<_>]> = Box::new_uninit_slice(total_len);
  47
  48        let mut index = 0usize;
  49        $(
  50            let array = $arr;
  51            for item in array {
  52                out[index].write(item);
  53                index += 1;
  54            }
  55        )+
  56
  57        debug_assert_eq!(index, total_len);
  58
  59        // SAFETY: we wrote exactly `total_len` elements.
  60        unsafe { out.assume_init() }
  61    }};
  62}
  63
  64pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
  65    vec![
  66        general_page(),
  67        appearance_page(),
  68        keymap_page(),
  69        editor_page(),
  70        languages_and_tools_page(cx),
  71        search_and_files_page(),
  72        window_and_layout_page(),
  73        panels_page(),
  74        debugger_page(),
  75        terminal_page(),
  76        version_control_page(),
  77        collaboration_page(),
  78        ai_page(cx),
  79        network_page(),
  80    ]
  81}
  82
  83fn general_page() -> SettingsPage {
  84    fn general_settings_section() -> [SettingsPageItem; 8] {
  85        [
  86            SettingsPageItem::SectionHeader("General Settings"),
  87            SettingsPageItem::SettingItem(SettingItem {
  88                files: PROJECT,
  89                title: "Project Name",
  90                description: "The displayed name of this project. If left empty, the root directory name will be displayed.",
  91                field: Box::new(SettingField {
  92                    json_path: Some("project_name"),
  93                    pick: |settings_content| {
  94                        settings_content
  95                            .project
  96                            .worktree
  97                            .project_name
  98                            .as_ref()
  99                            .or(DEFAULT_EMPTY_STRING)
 100                    },
 101                    write: |settings_content, value| {
 102                        settings_content.project.worktree.project_name =
 103                            value.filter(|name| !name.is_empty());
 104                    },
 105                }),
 106                metadata: Some(Box::new(SettingsFieldMetadata {
 107                    placeholder: Some("Project Name"),
 108                    ..Default::default()
 109                })),
 110            }),
 111            SettingsPageItem::SettingItem(SettingItem {
 112                title: "When Closing With No Tabs",
 113                description: "What to do when using the 'close active item' action with no tabs.",
 114                field: Box::new(SettingField {
 115                    json_path: Some("when_closing_with_no_tabs"),
 116                    pick: |settings_content| {
 117                        settings_content
 118                            .workspace
 119                            .when_closing_with_no_tabs
 120                            .as_ref()
 121                    },
 122                    write: |settings_content, value| {
 123                        settings_content.workspace.when_closing_with_no_tabs = value;
 124                    },
 125                }),
 126                metadata: None,
 127                files: USER,
 128            }),
 129            SettingsPageItem::SettingItem(SettingItem {
 130                title: "On Last Window Closed",
 131                description: "What to do when the last window is closed.",
 132                field: Box::new(SettingField {
 133                    json_path: Some("on_last_window_closed"),
 134                    pick: |settings_content| {
 135                        settings_content.workspace.on_last_window_closed.as_ref()
 136                    },
 137                    write: |settings_content, value| {
 138                        settings_content.workspace.on_last_window_closed = value;
 139                    },
 140                }),
 141                metadata: None,
 142                files: USER,
 143            }),
 144            SettingsPageItem::SettingItem(SettingItem {
 145                title: "Use System Path Prompts",
 146                description: "Use native OS dialogs for 'Open' and 'Save As'.",
 147                field: Box::new(SettingField {
 148                    json_path: Some("use_system_path_prompts"),
 149                    pick: |settings_content| {
 150                        settings_content.workspace.use_system_path_prompts.as_ref()
 151                    },
 152                    write: |settings_content, value| {
 153                        settings_content.workspace.use_system_path_prompts = value;
 154                    },
 155                }),
 156                metadata: None,
 157                files: USER,
 158            }),
 159            SettingsPageItem::SettingItem(SettingItem {
 160                title: "Use System Prompts",
 161                description: "Use native OS dialogs for confirmations.",
 162                field: Box::new(SettingField {
 163                    json_path: Some("use_system_prompts"),
 164                    pick: |settings_content| settings_content.workspace.use_system_prompts.as_ref(),
 165                    write: |settings_content, value| {
 166                        settings_content.workspace.use_system_prompts = value;
 167                    },
 168                }),
 169                metadata: None,
 170                files: USER,
 171            }),
 172            SettingsPageItem::SettingItem(SettingItem {
 173                title: "Redact Private Values",
 174                description: "Hide the values of variables in private files.",
 175                field: Box::new(SettingField {
 176                    json_path: Some("redact_private_values"),
 177                    pick: |settings_content| settings_content.editor.redact_private_values.as_ref(),
 178                    write: |settings_content, value| {
 179                        settings_content.editor.redact_private_values = value;
 180                    },
 181                }),
 182                metadata: None,
 183                files: USER,
 184            }),
 185            SettingsPageItem::SettingItem(SettingItem {
 186                title: "Private Files",
 187                description: "Globs to match against file paths to determine if a file is private.",
 188                field: Box::new(
 189                    SettingField {
 190                        json_path: Some("worktree.private_files"),
 191                        pick: |settings_content| {
 192                            settings_content.project.worktree.private_files.as_ref()
 193                        },
 194                        write: |settings_content, value| {
 195                            settings_content.project.worktree.private_files = value;
 196                        },
 197                    }
 198                    .unimplemented(),
 199                ),
 200                metadata: None,
 201                files: USER,
 202            }),
 203        ]
 204    }
 205    fn security_section() -> [SettingsPageItem; 2] {
 206        [
 207            SettingsPageItem::SectionHeader("Security"),
 208            SettingsPageItem::SettingItem(SettingItem {
 209                title: "Trust All Projects By Default",
 210                description: "When opening Zed, avoid Restricted Mode by auto-trusting all projects, enabling use of all features without having to give permission to each new project.",
 211                field: Box::new(SettingField {
 212                    json_path: Some("session.trust_all_projects"),
 213                    pick: |settings_content| {
 214                        settings_content
 215                            .session
 216                            .as_ref()
 217                            .and_then(|session| session.trust_all_worktrees.as_ref())
 218                    },
 219                    write: |settings_content, value| {
 220                        settings_content
 221                            .session
 222                            .get_or_insert_default()
 223                            .trust_all_worktrees = value;
 224                    },
 225                }),
 226                metadata: None,
 227                files: USER,
 228            }),
 229        ]
 230    }
 231
 232    fn workspace_restoration_section() -> [SettingsPageItem; 3] {
 233        [
 234            SettingsPageItem::SectionHeader("Workspace Restoration"),
 235            SettingsPageItem::SettingItem(SettingItem {
 236                title: "Restore Unsaved Buffers",
 237                description: "Whether or not to restore unsaved buffers on restart.",
 238                field: Box::new(SettingField {
 239                    json_path: Some("session.restore_unsaved_buffers"),
 240                    pick: |settings_content| {
 241                        settings_content
 242                            .session
 243                            .as_ref()
 244                            .and_then(|session| session.restore_unsaved_buffers.as_ref())
 245                    },
 246                    write: |settings_content, value| {
 247                        settings_content
 248                            .session
 249                            .get_or_insert_default()
 250                            .restore_unsaved_buffers = value;
 251                    },
 252                }),
 253                metadata: None,
 254                files: USER,
 255            }),
 256            SettingsPageItem::SettingItem(SettingItem {
 257                title: "Restore On Startup",
 258                description: "What to restore from the previous session when opening Zed.",
 259                field: Box::new(SettingField {
 260                    json_path: Some("restore_on_startup"),
 261                    pick: |settings_content| settings_content.workspace.restore_on_startup.as_ref(),
 262                    write: |settings_content, value| {
 263                        settings_content.workspace.restore_on_startup = value;
 264                    },
 265                }),
 266                metadata: None,
 267                files: USER,
 268            }),
 269        ]
 270    }
 271
 272    fn scoped_settings_section() -> [SettingsPageItem; 3] {
 273        [
 274            SettingsPageItem::SectionHeader("Scoped Settings"),
 275            SettingsPageItem::SettingItem(SettingItem {
 276                files: USER,
 277                title: "Preview Channel",
 278                description: "Which settings should be activated only in Preview build of Zed.",
 279                field: Box::new(
 280                    SettingField {
 281                        json_path: Some("preview_channel_settings"),
 282                        pick: |settings_content| Some(settings_content),
 283                        write: |_settings_content, _value| {},
 284                    }
 285                    .unimplemented(),
 286                ),
 287                metadata: None,
 288            }),
 289            SettingsPageItem::SettingItem(SettingItem {
 290                files: USER,
 291                title: "Settings Profiles",
 292                description: "Any number of settings profiles that are temporarily applied on top of your existing user settings.",
 293                field: Box::new(
 294                    SettingField {
 295                        json_path: Some("settings_profiles"),
 296                        pick: |settings_content| Some(settings_content),
 297                        write: |_settings_content, _value| {},
 298                    }
 299                    .unimplemented(),
 300                ),
 301                metadata: None,
 302            }),
 303        ]
 304    }
 305
 306    fn privacy_section() -> [SettingsPageItem; 3] {
 307        [
 308            SettingsPageItem::SectionHeader("Privacy"),
 309            SettingsPageItem::SettingItem(SettingItem {
 310                title: "Telemetry Diagnostics",
 311                description: "Send debug information like crash reports.",
 312                field: Box::new(SettingField {
 313                    json_path: Some("telemetry.diagnostics"),
 314                    pick: |settings_content| {
 315                        settings_content
 316                            .telemetry
 317                            .as_ref()
 318                            .and_then(|telemetry| telemetry.diagnostics.as_ref())
 319                    },
 320                    write: |settings_content, value| {
 321                        settings_content
 322                            .telemetry
 323                            .get_or_insert_default()
 324                            .diagnostics = value;
 325                    },
 326                }),
 327                metadata: None,
 328                files: USER,
 329            }),
 330            SettingsPageItem::SettingItem(SettingItem {
 331                title: "Telemetry Metrics",
 332                description: "Send anonymized usage data like what languages you're using Zed with.",
 333                field: Box::new(SettingField {
 334                    json_path: Some("telemetry.metrics"),
 335                    pick: |settings_content| {
 336                        settings_content
 337                            .telemetry
 338                            .as_ref()
 339                            .and_then(|telemetry| telemetry.metrics.as_ref())
 340                    },
 341                    write: |settings_content, value| {
 342                        settings_content.telemetry.get_or_insert_default().metrics = value;
 343                    },
 344                }),
 345                metadata: None,
 346                files: USER,
 347            }),
 348        ]
 349    }
 350
 351    fn auto_update_section() -> [SettingsPageItem; 2] {
 352        [
 353            SettingsPageItem::SectionHeader("Auto Update"),
 354            SettingsPageItem::SettingItem(SettingItem {
 355                title: "Auto Update",
 356                description: "Whether or not to automatically check for updates.",
 357                field: Box::new(SettingField {
 358                    json_path: Some("auto_update"),
 359                    pick: |settings_content| settings_content.auto_update.as_ref(),
 360                    write: |settings_content, value| {
 361                        settings_content.auto_update = value;
 362                    },
 363                }),
 364                metadata: None,
 365                files: USER,
 366            }),
 367        ]
 368    }
 369
 370    SettingsPage {
 371        title: "General",
 372        items: concat_sections!(
 373            general_settings_section(),
 374            security_section(),
 375            workspace_restoration_section(),
 376            scoped_settings_section(),
 377            privacy_section(),
 378            auto_update_section(),
 379        ),
 380    }
 381}
 382
 383fn appearance_page() -> SettingsPage {
 384    fn theme_section() -> [SettingsPageItem; 3] {
 385        [
 386            SettingsPageItem::SectionHeader("Theme"),
 387            SettingsPageItem::DynamicItem(DynamicItem {
 388                discriminant: SettingItem {
 389                    files: USER,
 390                    title: "Theme Mode",
 391                    description: "Choose a static, fixed theme or dynamically select themes based on appearance and light/dark modes.",
 392                    field: Box::new(SettingField {
 393                        json_path: Some("theme$"),
 394                        pick: |settings_content| {
 395                            Some(&dynamic_variants::<settings::ThemeSelection>()[
 396                                settings_content
 397                                    .theme
 398                                    .theme
 399                                    .as_ref()?
 400                                    .discriminant() as usize])
 401                        },
 402                        write: |settings_content, value| {
 403                            let Some(value) = value else {
 404                                settings_content.theme.theme = None;
 405                                return;
 406                            };
 407                            let settings_value = settings_content.theme.theme.get_or_insert_default();
 408                            *settings_value = match value {
 409                                settings::ThemeSelectionDiscriminants::Static => {
 410                                    let name = match settings_value {
 411                                        settings::ThemeSelection::Static(_) => return,
 412                                        settings::ThemeSelection::Dynamic { mode, light, dark } => {
 413                                            match mode {
 414                                                theme_settings::ThemeAppearanceMode::Light => light.clone(),
 415                                                theme_settings::ThemeAppearanceMode::Dark => dark.clone(),
 416                                                theme_settings::ThemeAppearanceMode::System => dark.clone(), // no cx, can't determine correct choice
 417                                            }
 418                                        },
 419                                    };
 420                                    settings::ThemeSelection::Static(name)
 421                                },
 422                                settings::ThemeSelectionDiscriminants::Dynamic => {
 423                                    let static_name = match settings_value {
 424                                        settings::ThemeSelection::Static(theme_name) => theme_name.clone(),
 425                                        settings::ThemeSelection::Dynamic {..} => return,
 426                                    };
 427
 428                                    settings::ThemeSelection::Dynamic {
 429                                        mode: settings::ThemeAppearanceMode::System,
 430                                        light: static_name.clone(),
 431                                        dark: static_name,
 432                                    }
 433                                },
 434                            };
 435                        },
 436                    }),
 437                    metadata: None,
 438                },
 439                pick_discriminant: |settings_content| {
 440                    Some(settings_content.theme.theme.as_ref()?.discriminant() as usize)
 441                },
 442                fields: dynamic_variants::<settings::ThemeSelection>().into_iter().map(|variant| {
 443                    match variant {
 444                        settings::ThemeSelectionDiscriminants::Static => vec![
 445                            SettingItem {
 446                                files: USER,
 447                                title: "Theme Name",
 448                                description: "The name of your selected theme.",
 449                                field: Box::new(SettingField {
 450                                    json_path: Some("theme"),
 451                                    pick: |settings_content| {
 452                                        match settings_content.theme.theme.as_ref() {
 453                                            Some(settings::ThemeSelection::Static(name)) => Some(name),
 454                                            _ => None
 455                                        }
 456                                    },
 457                                    write: |settings_content, value| {
 458                                        let Some(value) = value else {
 459                                            return;
 460                                        };
 461                                        match settings_content
 462                                            .theme
 463                                            .theme.get_or_insert_default() {
 464                                                settings::ThemeSelection::Static(theme_name) => *theme_name = value,
 465                                                _ => return
 466                                            }
 467                                    },
 468                                }),
 469                                metadata: None,
 470                            }
 471                        ],
 472                        settings::ThemeSelectionDiscriminants::Dynamic => vec![
 473                            SettingItem {
 474                                files: USER,
 475                                title: "Mode",
 476                                description: "Choose whether to use the selected light or dark theme or to follow your OS appearance configuration.",
 477                                field: Box::new(SettingField {
 478                                    json_path: Some("theme.mode"),
 479                                    pick: |settings_content| {
 480                                        match settings_content.theme.theme.as_ref() {
 481                                            Some(settings::ThemeSelection::Dynamic { mode, ..}) => Some(mode),
 482                                            _ => None
 483                                        }
 484                                    },
 485                                    write: |settings_content, value| {
 486                                        let Some(value) = value else {
 487                                            return;
 488                                        };
 489                                        match settings_content
 490                                            .theme
 491                                            .theme.get_or_insert_default() {
 492                                                settings::ThemeSelection::Dynamic{ mode, ..} => *mode = value,
 493                                                _ => return
 494                                            }
 495                                    },
 496                                }),
 497                                metadata: None,
 498                            },
 499                            SettingItem {
 500                                files: USER,
 501                                title: "Light Theme",
 502                                description: "The theme to use when mode is set to light, or when mode is set to system and it is in light mode.",
 503                                field: Box::new(SettingField {
 504                                    json_path: Some("theme.light"),
 505                                    pick: |settings_content| {
 506                                        match settings_content.theme.theme.as_ref() {
 507                                            Some(settings::ThemeSelection::Dynamic { light, ..}) => Some(light),
 508                                            _ => None
 509                                        }
 510                                    },
 511                                    write: |settings_content, value| {
 512                                        let Some(value) = value else {
 513                                            return;
 514                                        };
 515                                        match settings_content
 516                                            .theme
 517                                            .theme.get_or_insert_default() {
 518                                                settings::ThemeSelection::Dynamic{ light, ..} => *light = value,
 519                                                _ => return
 520                                            }
 521                                    },
 522                                }),
 523                                metadata: None,
 524                            },
 525                            SettingItem {
 526                                files: USER,
 527                                title: "Dark Theme",
 528                                description: "The theme to use when mode is set to dark, or when mode is set to system and it is in dark mode.",
 529                                field: Box::new(SettingField {
 530                                    json_path: Some("theme.dark"),
 531                                    pick: |settings_content| {
 532                                        match settings_content.theme.theme.as_ref() {
 533                                            Some(settings::ThemeSelection::Dynamic { dark, ..}) => Some(dark),
 534                                            _ => None
 535                                        }
 536                                    },
 537                                    write: |settings_content, value| {
 538                                        let Some(value) = value else {
 539                                            return;
 540                                        };
 541                                        match settings_content
 542                                            .theme
 543                                            .theme.get_or_insert_default() {
 544                                                settings::ThemeSelection::Dynamic{ dark, ..} => *dark = value,
 545                                                _ => return
 546                                            }
 547                                    },
 548                                }),
 549                                metadata: None,
 550                            }
 551                        ],
 552                    }
 553                }).collect(),
 554            }),
 555            SettingsPageItem::DynamicItem(DynamicItem {
 556                discriminant: SettingItem {
 557                    files: USER,
 558                    title: "Icon Theme",
 559                    description: "The custom set of icons Zed will associate with files and directories.",
 560                    field: Box::new(SettingField {
 561                        json_path: Some("icon_theme$"),
 562                        pick: |settings_content| {
 563                            Some(&dynamic_variants::<settings::IconThemeSelection>()[
 564                                settings_content
 565                                    .theme
 566                                    .icon_theme
 567                                    .as_ref()?
 568                                    .discriminant() as usize])
 569                        },
 570                        write: |settings_content, value| {
 571                            let Some(value) = value else {
 572                                settings_content.theme.icon_theme = None;
 573                                return;
 574                            };
 575                            let settings_value = settings_content.theme.icon_theme.get_or_insert_with(|| {
 576                                settings::IconThemeSelection::Static(settings::IconThemeName(theme::default_icon_theme().name.clone().into()))
 577                            });
 578                            *settings_value = match value {
 579                                settings::IconThemeSelectionDiscriminants::Static => {
 580                                    let name = match settings_value {
 581                                        settings::IconThemeSelection::Static(_) => return,
 582                                        settings::IconThemeSelection::Dynamic { mode, light, dark } => {
 583                                            match mode {
 584                                                theme_settings::ThemeAppearanceMode::Light => light.clone(),
 585                                                theme_settings::ThemeAppearanceMode::Dark => dark.clone(),
 586                                                theme_settings::ThemeAppearanceMode::System => dark.clone(), // no cx, can't determine correct choice
 587                                            }
 588                                        },
 589                                    };
 590                                    settings::IconThemeSelection::Static(name)
 591                                },
 592                                settings::IconThemeSelectionDiscriminants::Dynamic => {
 593                                    let static_name = match settings_value {
 594                                        settings::IconThemeSelection::Static(theme_name) => theme_name.clone(),
 595                                        settings::IconThemeSelection::Dynamic {..} => return,
 596                                    };
 597
 598                                    settings::IconThemeSelection::Dynamic {
 599                                        mode: settings::ThemeAppearanceMode::System,
 600                                        light: static_name.clone(),
 601                                        dark: static_name,
 602                                    }
 603                                },
 604                            };
 605                        },
 606                    }),
 607                    metadata: None,
 608                },
 609                pick_discriminant: |settings_content| {
 610                    Some(settings_content.theme.icon_theme.as_ref()?.discriminant() as usize)
 611                },
 612                fields: dynamic_variants::<settings::IconThemeSelection>().into_iter().map(|variant| {
 613                    match variant {
 614                        settings::IconThemeSelectionDiscriminants::Static => vec![
 615                            SettingItem {
 616                                files: USER,
 617                                title: "Icon Theme Name",
 618                                description: "The name of your selected icon theme.",
 619                                field: Box::new(SettingField {
 620                                    json_path: Some("icon_theme$string"),
 621                                    pick: |settings_content| {
 622                                        match settings_content.theme.icon_theme.as_ref() {
 623                                            Some(settings::IconThemeSelection::Static(name)) => Some(name),
 624                                            _ => None
 625                                        }
 626                                    },
 627                                    write: |settings_content, value| {
 628                                        let Some(value) = value else {
 629                                            return;
 630                                        };
 631                                        match settings_content
 632                                            .theme
 633                                            .icon_theme.as_mut() {
 634                                                Some(settings::IconThemeSelection::Static(theme_name)) => *theme_name = value,
 635                                                _ => return
 636                                            }
 637                                    },
 638                                }),
 639                                metadata: None,
 640                            }
 641                        ],
 642                        settings::IconThemeSelectionDiscriminants::Dynamic => vec![
 643                            SettingItem {
 644                                files: USER,
 645                                title: "Mode",
 646                                description: "Choose whether to use the selected light or dark icon theme or to follow your OS appearance configuration.",
 647                                field: Box::new(SettingField {
 648                                    json_path: Some("icon_theme"),
 649                                    pick: |settings_content| {
 650                                        match settings_content.theme.icon_theme.as_ref() {
 651                                            Some(settings::IconThemeSelection::Dynamic { mode, ..}) => Some(mode),
 652                                            _ => None
 653                                        }
 654                                    },
 655                                    write: |settings_content, value| {
 656                                        let Some(value) = value else {
 657                                            return;
 658                                        };
 659                                        match settings_content
 660                                            .theme
 661                                            .icon_theme.as_mut() {
 662                                                Some(settings::IconThemeSelection::Dynamic{ mode, ..}) => *mode = value,
 663                                                _ => return
 664                                            }
 665                                    },
 666                                }),
 667                                metadata: None,
 668                            },
 669                            SettingItem {
 670                                files: USER,
 671                                title: "Light Icon Theme",
 672                                description: "The icon theme to use when mode is set to light, or when mode is set to system and it is in light mode.",
 673                                field: Box::new(SettingField {
 674                                    json_path: Some("icon_theme.light"),
 675                                    pick: |settings_content| {
 676                                        match settings_content.theme.icon_theme.as_ref() {
 677                                            Some(settings::IconThemeSelection::Dynamic { light, ..}) => Some(light),
 678                                            _ => None
 679                                        }
 680                                    },
 681                                    write: |settings_content, value| {
 682                                        let Some(value) = value else {
 683                                            return;
 684                                        };
 685                                        match settings_content
 686                                            .theme
 687                                            .icon_theme.as_mut() {
 688                                                Some(settings::IconThemeSelection::Dynamic{ light, ..}) => *light = value,
 689                                                _ => return
 690                                            }
 691                                    },
 692                                }),
 693                                metadata: None,
 694                            },
 695                            SettingItem {
 696                                files: USER,
 697                                title: "Dark Icon Theme",
 698                                description: "The icon theme to use when mode is set to dark, or when mode is set to system and it is in dark mode.",
 699                                field: Box::new(SettingField {
 700                                    json_path: Some("icon_theme.dark"),
 701                                    pick: |settings_content| {
 702                                        match settings_content.theme.icon_theme.as_ref() {
 703                                            Some(settings::IconThemeSelection::Dynamic { dark, ..}) => Some(dark),
 704                                            _ => None
 705                                        }
 706                                    },
 707                                    write: |settings_content, value| {
 708                                        let Some(value) = value else {
 709                                            return;
 710                                        };
 711                                        match settings_content
 712                                            .theme
 713                                            .icon_theme.as_mut() {
 714                                                Some(settings::IconThemeSelection::Dynamic{ dark, ..}) => *dark = value,
 715                                                _ => return
 716                                            }
 717                                    },
 718                                }),
 719                                metadata: None,
 720                            }
 721                        ],
 722                    }
 723                }).collect(),
 724            }),
 725        ]
 726    }
 727
 728    fn buffer_font_section() -> [SettingsPageItem; 7] {
 729        [
 730            SettingsPageItem::SectionHeader("Buffer Font"),
 731            SettingsPageItem::SettingItem(SettingItem {
 732                title: "Font Family",
 733                description: "Font family for editor text.",
 734                field: Box::new(SettingField {
 735                    json_path: Some("buffer_font_family"),
 736                    pick: |settings_content| settings_content.theme.buffer_font_family.as_ref(),
 737                    write: |settings_content, value| {
 738                        settings_content.theme.buffer_font_family = value;
 739                    },
 740                }),
 741                metadata: None,
 742                files: USER,
 743            }),
 744            SettingsPageItem::SettingItem(SettingItem {
 745                title: "Font Size",
 746                description: "Font size for editor text.",
 747                field: Box::new(SettingField {
 748                    json_path: Some("buffer_font_size"),
 749                    pick: |settings_content| settings_content.theme.buffer_font_size.as_ref(),
 750                    write: |settings_content, value| {
 751                        settings_content.theme.buffer_font_size = value;
 752                    },
 753                }),
 754                metadata: None,
 755                files: USER,
 756            }),
 757            SettingsPageItem::SettingItem(SettingItem {
 758                title: "Font Weight",
 759                description: "Font weight for editor text (100-900).",
 760                field: Box::new(SettingField {
 761                    json_path: Some("buffer_font_weight"),
 762                    pick: |settings_content| settings_content.theme.buffer_font_weight.as_ref(),
 763                    write: |settings_content, value| {
 764                        settings_content.theme.buffer_font_weight = value;
 765                    },
 766                }),
 767                metadata: None,
 768                files: USER,
 769            }),
 770            SettingsPageItem::DynamicItem(DynamicItem {
 771                discriminant: SettingItem {
 772                    files: USER,
 773                    title: "Line Height",
 774                    description: "Line height for editor text.",
 775                    field: Box::new(SettingField {
 776                        json_path: Some("buffer_line_height$"),
 777                        pick: |settings_content| {
 778                            Some(
 779                                &dynamic_variants::<settings::BufferLineHeight>()[settings_content
 780                                    .theme
 781                                    .buffer_line_height
 782                                    .as_ref()?
 783                                    .discriminant()
 784                                    as usize],
 785                            )
 786                        },
 787                        write: |settings_content, value| {
 788                            let Some(value) = value else {
 789                                settings_content.theme.buffer_line_height = None;
 790                                return;
 791                            };
 792                            let settings_value = settings_content
 793                                .theme
 794                                .buffer_line_height
 795                                .get_or_insert_with(|| settings::BufferLineHeight::default());
 796                            *settings_value = match value {
 797                                settings::BufferLineHeightDiscriminants::Comfortable => {
 798                                    settings::BufferLineHeight::Comfortable
 799                                }
 800                                settings::BufferLineHeightDiscriminants::Standard => {
 801                                    settings::BufferLineHeight::Standard
 802                                }
 803                                settings::BufferLineHeightDiscriminants::Custom => {
 804                                    let custom_value =
 805                                        theme_settings::BufferLineHeight::from(*settings_value)
 806                                            .value();
 807                                    settings::BufferLineHeight::Custom(custom_value)
 808                                }
 809                            };
 810                        },
 811                    }),
 812                    metadata: None,
 813                },
 814                pick_discriminant: |settings_content| {
 815                    Some(
 816                        settings_content
 817                            .theme
 818                            .buffer_line_height
 819                            .as_ref()?
 820                            .discriminant() as usize,
 821                    )
 822                },
 823                fields: dynamic_variants::<settings::BufferLineHeight>()
 824                    .into_iter()
 825                    .map(|variant| match variant {
 826                        settings::BufferLineHeightDiscriminants::Comfortable => vec![],
 827                        settings::BufferLineHeightDiscriminants::Standard => vec![],
 828                        settings::BufferLineHeightDiscriminants::Custom => vec![SettingItem {
 829                            files: USER,
 830                            title: "Custom Line Height",
 831                            description: "Custom line height value (must be at least 1.0).",
 832                            field: Box::new(SettingField {
 833                                json_path: Some("buffer_line_height"),
 834                                pick: |settings_content| match settings_content
 835                                    .theme
 836                                    .buffer_line_height
 837                                    .as_ref()
 838                                {
 839                                    Some(settings::BufferLineHeight::Custom(value)) => Some(value),
 840                                    _ => None,
 841                                },
 842                                write: |settings_content, value| {
 843                                    let Some(value) = value else {
 844                                        return;
 845                                    };
 846                                    match settings_content.theme.buffer_line_height.as_mut() {
 847                                        Some(settings::BufferLineHeight::Custom(line_height)) => {
 848                                            *line_height = f32::max(value, 1.0)
 849                                        }
 850                                        _ => return,
 851                                    }
 852                                },
 853                            }),
 854                            metadata: None,
 855                        }],
 856                    })
 857                    .collect(),
 858            }),
 859            SettingsPageItem::SettingItem(SettingItem {
 860                files: USER,
 861                title: "Font Features",
 862                description: "The OpenType features to enable for rendering in text buffers.",
 863                field: Box::new(
 864                    SettingField {
 865                        json_path: Some("buffer_font_features"),
 866                        pick: |settings_content| {
 867                            settings_content.theme.buffer_font_features.as_ref()
 868                        },
 869                        write: |settings_content, value| {
 870                            settings_content.theme.buffer_font_features = value;
 871                        },
 872                    }
 873                    .unimplemented(),
 874                ),
 875                metadata: None,
 876            }),
 877            SettingsPageItem::SettingItem(SettingItem {
 878                files: USER,
 879                title: "Font Fallbacks",
 880                description: "The font fallbacks to use for rendering in text buffers.",
 881                field: Box::new(
 882                    SettingField {
 883                        json_path: Some("buffer_font_fallbacks"),
 884                        pick: |settings_content| {
 885                            settings_content.theme.buffer_font_fallbacks.as_ref()
 886                        },
 887                        write: |settings_content, value| {
 888                            settings_content.theme.buffer_font_fallbacks = value;
 889                        },
 890                    }
 891                    .unimplemented(),
 892                ),
 893                metadata: None,
 894            }),
 895        ]
 896    }
 897
 898    fn ui_font_section() -> [SettingsPageItem; 6] {
 899        [
 900            SettingsPageItem::SectionHeader("UI Font"),
 901            SettingsPageItem::SettingItem(SettingItem {
 902                title: "Font Family",
 903                description: "Font family for UI elements.",
 904                field: Box::new(SettingField {
 905                    json_path: Some("ui_font_family"),
 906                    pick: |settings_content| settings_content.theme.ui_font_family.as_ref(),
 907                    write: |settings_content, value| {
 908                        settings_content.theme.ui_font_family = value;
 909                    },
 910                }),
 911                metadata: None,
 912                files: USER,
 913            }),
 914            SettingsPageItem::SettingItem(SettingItem {
 915                title: "Font Size",
 916                description: "Font size for UI elements.",
 917                field: Box::new(SettingField {
 918                    json_path: Some("ui_font_size"),
 919                    pick: |settings_content| settings_content.theme.ui_font_size.as_ref(),
 920                    write: |settings_content, value| {
 921                        settings_content.theme.ui_font_size = value;
 922                    },
 923                }),
 924                metadata: None,
 925                files: USER,
 926            }),
 927            SettingsPageItem::SettingItem(SettingItem {
 928                title: "Font Weight",
 929                description: "Font weight for UI elements (100-900).",
 930                field: Box::new(SettingField {
 931                    json_path: Some("ui_font_weight"),
 932                    pick: |settings_content| settings_content.theme.ui_font_weight.as_ref(),
 933                    write: |settings_content, value| {
 934                        settings_content.theme.ui_font_weight = value;
 935                    },
 936                }),
 937                metadata: None,
 938                files: USER,
 939            }),
 940            SettingsPageItem::SettingItem(SettingItem {
 941                files: USER,
 942                title: "Font Features",
 943                description: "The OpenType features to enable for rendering in UI elements.",
 944                field: Box::new(
 945                    SettingField {
 946                        json_path: Some("ui_font_features"),
 947                        pick: |settings_content| settings_content.theme.ui_font_features.as_ref(),
 948                        write: |settings_content, value| {
 949                            settings_content.theme.ui_font_features = value;
 950                        },
 951                    }
 952                    .unimplemented(),
 953                ),
 954                metadata: None,
 955            }),
 956            SettingsPageItem::SettingItem(SettingItem {
 957                files: USER,
 958                title: "Font Fallbacks",
 959                description: "The font fallbacks to use for rendering in the UI.",
 960                field: Box::new(
 961                    SettingField {
 962                        json_path: Some("ui_font_fallbacks"),
 963                        pick: |settings_content| settings_content.theme.ui_font_fallbacks.as_ref(),
 964                        write: |settings_content, value| {
 965                            settings_content.theme.ui_font_fallbacks = value;
 966                        },
 967                    }
 968                    .unimplemented(),
 969                ),
 970                metadata: None,
 971            }),
 972        ]
 973    }
 974
 975    fn agent_panel_font_section() -> [SettingsPageItem; 3] {
 976        [
 977            SettingsPageItem::SectionHeader("Agent Panel Font"),
 978            SettingsPageItem::SettingItem(SettingItem {
 979                title: "UI Font Size",
 980                description: "Font size for agent response text in the agent panel. Falls back to the regular UI font size.",
 981                field: Box::new(SettingField {
 982                    json_path: Some("agent_ui_font_size"),
 983                    pick: |settings_content| {
 984                        settings_content
 985                            .theme
 986                            .agent_ui_font_size
 987                            .as_ref()
 988                            .or(settings_content.theme.ui_font_size.as_ref())
 989                    },
 990                    write: |settings_content, value| {
 991                        settings_content.theme.agent_ui_font_size = value;
 992                    },
 993                }),
 994                metadata: None,
 995                files: USER,
 996            }),
 997            SettingsPageItem::SettingItem(SettingItem {
 998                title: "Buffer Font Size",
 999                description: "Font size for user messages text in the agent panel.",
1000                field: Box::new(SettingField {
1001                    json_path: Some("agent_buffer_font_size"),
1002                    pick: |settings_content| {
1003                        settings_content
1004                            .theme
1005                            .agent_buffer_font_size
1006                            .as_ref()
1007                            .or(settings_content.theme.buffer_font_size.as_ref())
1008                    },
1009                    write: |settings_content, value| {
1010                        settings_content.theme.agent_buffer_font_size = value;
1011                    },
1012                }),
1013                metadata: None,
1014                files: USER,
1015            }),
1016        ]
1017    }
1018
1019    fn text_rendering_section() -> [SettingsPageItem; 2] {
1020        [
1021            SettingsPageItem::SectionHeader("Text Rendering"),
1022            SettingsPageItem::SettingItem(SettingItem {
1023                title: "Text Rendering Mode",
1024                description: "The text rendering mode to use.",
1025                field: Box::new(SettingField {
1026                    json_path: Some("text_rendering_mode"),
1027                    pick: |settings_content| {
1028                        settings_content.workspace.text_rendering_mode.as_ref()
1029                    },
1030                    write: |settings_content, value| {
1031                        settings_content.workspace.text_rendering_mode = value;
1032                    },
1033                }),
1034                metadata: None,
1035                files: USER,
1036            }),
1037        ]
1038    }
1039
1040    fn cursor_section() -> [SettingsPageItem; 5] {
1041        [
1042            SettingsPageItem::SectionHeader("Cursor"),
1043            SettingsPageItem::SettingItem(SettingItem {
1044                title: "Multi Cursor Modifier",
1045                description: "Modifier key for adding multiple cursors.",
1046                field: Box::new(SettingField {
1047                    json_path: Some("multi_cursor_modifier"),
1048                    pick: |settings_content| settings_content.editor.multi_cursor_modifier.as_ref(),
1049                    write: |settings_content, value| {
1050                        settings_content.editor.multi_cursor_modifier = value;
1051                    },
1052                }),
1053                metadata: None,
1054                files: USER,
1055            }),
1056            SettingsPageItem::SettingItem(SettingItem {
1057                title: "Cursor Blink",
1058                description: "Whether the cursor blinks in the editor.",
1059                field: Box::new(SettingField {
1060                    json_path: Some("cursor_blink"),
1061                    pick: |settings_content| settings_content.editor.cursor_blink.as_ref(),
1062                    write: |settings_content, value| {
1063                        settings_content.editor.cursor_blink = value;
1064                    },
1065                }),
1066                metadata: None,
1067                files: USER,
1068            }),
1069            SettingsPageItem::SettingItem(SettingItem {
1070                title: "Cursor Shape",
1071                description: "Cursor shape for the editor.",
1072                field: Box::new(SettingField {
1073                    json_path: Some("cursor_shape"),
1074                    pick: |settings_content| settings_content.editor.cursor_shape.as_ref(),
1075                    write: |settings_content, value| {
1076                        settings_content.editor.cursor_shape = value;
1077                    },
1078                }),
1079                metadata: None,
1080                files: USER,
1081            }),
1082            SettingsPageItem::SettingItem(SettingItem {
1083                title: "Hide Mouse",
1084                description: "When to hide the mouse cursor.",
1085                field: Box::new(SettingField {
1086                    json_path: Some("hide_mouse"),
1087                    pick: |settings_content| settings_content.editor.hide_mouse.as_ref(),
1088                    write: |settings_content, value| {
1089                        settings_content.editor.hide_mouse = value;
1090                    },
1091                }),
1092                metadata: None,
1093                files: USER,
1094            }),
1095        ]
1096    }
1097
1098    fn highlighting_section() -> [SettingsPageItem; 6] {
1099        [
1100            SettingsPageItem::SectionHeader("Highlighting"),
1101            SettingsPageItem::SettingItem(SettingItem {
1102                title: "Unnecessary Code Fade",
1103                description: "How much to fade out unused code (0.0 - 0.9).",
1104                field: Box::new(SettingField {
1105                    json_path: Some("unnecessary_code_fade"),
1106                    pick: |settings_content| settings_content.theme.unnecessary_code_fade.as_ref(),
1107                    write: |settings_content, value| {
1108                        settings_content.theme.unnecessary_code_fade = value;
1109                    },
1110                }),
1111                metadata: None,
1112                files: USER,
1113            }),
1114            SettingsPageItem::SettingItem(SettingItem {
1115                title: "Current Line Highlight",
1116                description: "How to highlight the current line.",
1117                field: Box::new(SettingField {
1118                    json_path: Some("current_line_highlight"),
1119                    pick: |settings_content| {
1120                        settings_content.editor.current_line_highlight.as_ref()
1121                    },
1122                    write: |settings_content, value| {
1123                        settings_content.editor.current_line_highlight = value;
1124                    },
1125                }),
1126                metadata: None,
1127                files: USER,
1128            }),
1129            SettingsPageItem::SettingItem(SettingItem {
1130                title: "Selection Highlight",
1131                description: "Highlight all occurrences of selected text.",
1132                field: Box::new(SettingField {
1133                    json_path: Some("selection_highlight"),
1134                    pick: |settings_content| settings_content.editor.selection_highlight.as_ref(),
1135                    write: |settings_content, value| {
1136                        settings_content.editor.selection_highlight = value;
1137                    },
1138                }),
1139                metadata: None,
1140                files: USER,
1141            }),
1142            SettingsPageItem::SettingItem(SettingItem {
1143                title: "Rounded Selection",
1144                description: "Whether the text selection should have rounded corners.",
1145                field: Box::new(SettingField {
1146                    json_path: Some("rounded_selection"),
1147                    pick: |settings_content| settings_content.editor.rounded_selection.as_ref(),
1148                    write: |settings_content, value| {
1149                        settings_content.editor.rounded_selection = value;
1150                    },
1151                }),
1152                metadata: None,
1153                files: USER,
1154            }),
1155            SettingsPageItem::SettingItem(SettingItem {
1156                title: "Minimum Contrast For Highlights",
1157                description: "The minimum APCA perceptual contrast to maintain when rendering text over highlight backgrounds.",
1158                field: Box::new(SettingField {
1159                    json_path: Some("minimum_contrast_for_highlights"),
1160                    pick: |settings_content| {
1161                        settings_content
1162                            .editor
1163                            .minimum_contrast_for_highlights
1164                            .as_ref()
1165                    },
1166                    write: |settings_content, value| {
1167                        settings_content.editor.minimum_contrast_for_highlights = value;
1168                    },
1169                }),
1170                metadata: None,
1171                files: USER,
1172            }),
1173        ]
1174    }
1175
1176    fn guides_section() -> [SettingsPageItem; 3] {
1177        [
1178            SettingsPageItem::SectionHeader("Guides"),
1179            SettingsPageItem::SettingItem(SettingItem {
1180                title: "Show Wrap Guides",
1181                description: "Show wrap guides (vertical rulers).",
1182                field: Box::new(SettingField {
1183                    json_path: Some("show_wrap_guides"),
1184                    pick: |settings_content| {
1185                        settings_content
1186                            .project
1187                            .all_languages
1188                            .defaults
1189                            .show_wrap_guides
1190                            .as_ref()
1191                    },
1192                    write: |settings_content, value| {
1193                        settings_content
1194                            .project
1195                            .all_languages
1196                            .defaults
1197                            .show_wrap_guides = value;
1198                    },
1199                }),
1200                metadata: None,
1201                files: USER | PROJECT,
1202            }),
1203            // todo(settings_ui): This needs a custom component
1204            SettingsPageItem::SettingItem(SettingItem {
1205                title: "Wrap Guides",
1206                description: "Character counts at which to show wrap guides.",
1207                field: Box::new(
1208                    SettingField {
1209                        json_path: Some("wrap_guides"),
1210                        pick: |settings_content| {
1211                            settings_content
1212                                .project
1213                                .all_languages
1214                                .defaults
1215                                .wrap_guides
1216                                .as_ref()
1217                        },
1218                        write: |settings_content, value| {
1219                            settings_content.project.all_languages.defaults.wrap_guides = value;
1220                        },
1221                    }
1222                    .unimplemented(),
1223                ),
1224                metadata: None,
1225                files: USER | PROJECT,
1226            }),
1227        ]
1228    }
1229
1230    let items: Box<[SettingsPageItem]> = concat_sections!(
1231        theme_section(),
1232        buffer_font_section(),
1233        ui_font_section(),
1234        agent_panel_font_section(),
1235        text_rendering_section(),
1236        cursor_section(),
1237        highlighting_section(),
1238        guides_section(),
1239    );
1240
1241    SettingsPage {
1242        title: "Appearance",
1243        items,
1244    }
1245}
1246
1247fn keymap_page() -> SettingsPage {
1248    fn keybindings_section() -> [SettingsPageItem; 2] {
1249        [
1250            SettingsPageItem::SectionHeader("Keybindings"),
1251            SettingsPageItem::ActionLink(ActionLink {
1252                title: "Edit Keybindings".into(),
1253                description: Some("Customize keybindings in the keymap editor.".into()),
1254                button_text: "Open Keymap".into(),
1255                on_click: Arc::new(|settings_window, window, cx| {
1256                    let Some(original_window) = settings_window.original_window else {
1257                        return;
1258                    };
1259                    original_window
1260                        .update(cx, |_workspace, original_window, cx| {
1261                            original_window
1262                                .dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx);
1263                            original_window.activate_window();
1264                        })
1265                        .ok();
1266                    window.remove_window();
1267                }),
1268                files: USER,
1269            }),
1270        ]
1271    }
1272
1273    fn base_keymap_section() -> [SettingsPageItem; 2] {
1274        [
1275            SettingsPageItem::SectionHeader("Base Keymap"),
1276            SettingsPageItem::SettingItem(SettingItem {
1277                title: "Base Keymap",
1278                description: "The name of a base set of key bindings to use.",
1279                field: Box::new(SettingField {
1280                    json_path: Some("base_keymap"),
1281                    pick: |settings_content| settings_content.base_keymap.as_ref(),
1282                    write: |settings_content, value| {
1283                        settings_content.base_keymap = value;
1284                    },
1285                }),
1286                metadata: Some(Box::new(SettingsFieldMetadata {
1287                    should_do_titlecase: Some(false),
1288                    ..Default::default()
1289                })),
1290                files: USER,
1291            }),
1292        ]
1293    }
1294
1295    fn modal_editing_section() -> [SettingsPageItem; 3] {
1296        [
1297            SettingsPageItem::SectionHeader("Modal Editing"),
1298            SettingsPageItem::SettingItem(SettingItem {
1299                title: "Vim Mode",
1300                description: "Enable Vim mode and key bindings.",
1301                field: Box::new(SettingField {
1302                    json_path: Some("vim_mode"),
1303                    pick: |settings_content| settings_content.vim_mode.as_ref(),
1304                    write: write_vim_mode,
1305                }),
1306                metadata: None,
1307                files: USER,
1308            }),
1309            SettingsPageItem::SettingItem(SettingItem {
1310                title: "Helix Mode",
1311                description: "Enable Helix mode and key bindings.",
1312                field: Box::new(SettingField {
1313                    json_path: Some("helix_mode"),
1314                    pick: |settings_content| settings_content.helix_mode.as_ref(),
1315                    write: write_helix_mode,
1316                }),
1317                metadata: None,
1318                files: USER,
1319            }),
1320        ]
1321    }
1322
1323    let items: Box<[SettingsPageItem]> = concat_sections!(
1324        keybindings_section(),
1325        base_keymap_section(),
1326        modal_editing_section(),
1327    );
1328
1329    SettingsPage {
1330        title: "Keymap",
1331        items,
1332    }
1333}
1334
1335fn editor_page() -> SettingsPage {
1336    fn auto_save_section() -> [SettingsPageItem; 2] {
1337        [
1338            SettingsPageItem::SectionHeader("Auto Save"),
1339            SettingsPageItem::DynamicItem(DynamicItem {
1340                discriminant: SettingItem {
1341                    files: USER,
1342                    title: "Auto Save Mode",
1343                    description: "When to auto save buffer changes.",
1344                    field: Box::new(SettingField {
1345                        json_path: Some("autosave$"),
1346                        pick: |settings_content| {
1347                            Some(
1348                                &dynamic_variants::<settings::AutosaveSetting>()[settings_content
1349                                    .workspace
1350                                    .autosave
1351                                    .as_ref()?
1352                                    .discriminant()
1353                                    as usize],
1354                            )
1355                        },
1356                        write: |settings_content, value| {
1357                            let Some(value) = value else {
1358                                settings_content.workspace.autosave = None;
1359                                return;
1360                            };
1361                            let settings_value = settings_content
1362                                .workspace
1363                                .autosave
1364                                .get_or_insert_with(|| settings::AutosaveSetting::Off);
1365                            *settings_value = match value {
1366                                settings::AutosaveSettingDiscriminants::Off => {
1367                                    settings::AutosaveSetting::Off
1368                                }
1369                                settings::AutosaveSettingDiscriminants::AfterDelay => {
1370                                    let milliseconds = match settings_value {
1371                                        settings::AutosaveSetting::AfterDelay { milliseconds } => {
1372                                            *milliseconds
1373                                        }
1374                                        _ => settings::DelayMs(1000),
1375                                    };
1376                                    settings::AutosaveSetting::AfterDelay { milliseconds }
1377                                }
1378                                settings::AutosaveSettingDiscriminants::OnFocusChange => {
1379                                    settings::AutosaveSetting::OnFocusChange
1380                                }
1381                                settings::AutosaveSettingDiscriminants::OnWindowChange => {
1382                                    settings::AutosaveSetting::OnWindowChange
1383                                }
1384                            };
1385                        },
1386                    }),
1387                    metadata: None,
1388                },
1389                pick_discriminant: |settings_content| {
1390                    Some(settings_content.workspace.autosave.as_ref()?.discriminant() as usize)
1391                },
1392                fields: dynamic_variants::<settings::AutosaveSetting>()
1393                    .into_iter()
1394                    .map(|variant| match variant {
1395                        settings::AutosaveSettingDiscriminants::Off => vec![],
1396                        settings::AutosaveSettingDiscriminants::AfterDelay => vec![SettingItem {
1397                            files: USER,
1398                            title: "Delay (milliseconds)",
1399                            description: "Save after inactivity period (in milliseconds).",
1400                            field: Box::new(SettingField {
1401                                json_path: Some("autosave.after_delay.milliseconds"),
1402                                pick: |settings_content| match settings_content
1403                                    .workspace
1404                                    .autosave
1405                                    .as_ref()
1406                                {
1407                                    Some(settings::AutosaveSetting::AfterDelay {
1408                                        milliseconds,
1409                                    }) => Some(milliseconds),
1410                                    _ => None,
1411                                },
1412                                write: |settings_content, value| {
1413                                    let Some(value) = value else {
1414                                        settings_content.workspace.autosave = None;
1415                                        return;
1416                                    };
1417                                    match settings_content.workspace.autosave.as_mut() {
1418                                        Some(settings::AutosaveSetting::AfterDelay {
1419                                            milliseconds,
1420                                        }) => *milliseconds = value,
1421                                        _ => return,
1422                                    }
1423                                },
1424                            }),
1425                            metadata: None,
1426                        }],
1427                        settings::AutosaveSettingDiscriminants::OnFocusChange => vec![],
1428                        settings::AutosaveSettingDiscriminants::OnWindowChange => vec![],
1429                    })
1430                    .collect(),
1431            }),
1432        ]
1433    }
1434
1435    fn which_key_section() -> [SettingsPageItem; 3] {
1436        [
1437            SettingsPageItem::SectionHeader("Which-key Menu"),
1438            SettingsPageItem::SettingItem(SettingItem {
1439                title: "Show Which-key Menu",
1440                description: "Display the which-key menu with matching bindings while a multi-stroke binding is pending.",
1441                field: Box::new(SettingField {
1442                    json_path: Some("which_key.enabled"),
1443                    pick: |settings_content| {
1444                        settings_content
1445                            .which_key
1446                            .as_ref()
1447                            .and_then(|settings| settings.enabled.as_ref())
1448                    },
1449                    write: |settings_content, value| {
1450                        settings_content.which_key.get_or_insert_default().enabled = value;
1451                    },
1452                }),
1453                metadata: None,
1454                files: USER,
1455            }),
1456            SettingsPageItem::SettingItem(SettingItem {
1457                title: "Menu Delay",
1458                description: "Delay in milliseconds before the which-key menu appears.",
1459                field: Box::new(SettingField {
1460                    json_path: Some("which_key.delay_ms"),
1461                    pick: |settings_content| {
1462                        settings_content
1463                            .which_key
1464                            .as_ref()
1465                            .and_then(|settings| settings.delay_ms.as_ref())
1466                    },
1467                    write: |settings_content, value| {
1468                        settings_content.which_key.get_or_insert_default().delay_ms = value;
1469                    },
1470                }),
1471                metadata: None,
1472                files: USER,
1473            }),
1474        ]
1475    }
1476
1477    fn multibuffer_section() -> [SettingsPageItem; 7] {
1478        [
1479            SettingsPageItem::SectionHeader("Multibuffer"),
1480            SettingsPageItem::SettingItem(SettingItem {
1481                title: "Double Click In Multibuffer",
1482                description: "What to do when multibuffer is double-clicked in some of its excerpts.",
1483                field: Box::new(SettingField {
1484                    json_path: Some("double_click_in_multibuffer"),
1485                    pick: |settings_content| {
1486                        settings_content.editor.double_click_in_multibuffer.as_ref()
1487                    },
1488                    write: |settings_content, value| {
1489                        settings_content.editor.double_click_in_multibuffer = value;
1490                    },
1491                }),
1492                metadata: None,
1493                files: USER,
1494            }),
1495            SettingsPageItem::SettingItem(SettingItem {
1496                title: "Expand Excerpt Lines",
1497                description: "How many lines to expand the multibuffer excerpts by default.",
1498                field: Box::new(SettingField {
1499                    json_path: Some("expand_excerpt_lines"),
1500                    pick: |settings_content| settings_content.editor.expand_excerpt_lines.as_ref(),
1501                    write: |settings_content, value| {
1502                        settings_content.editor.expand_excerpt_lines = value;
1503                    },
1504                }),
1505                metadata: None,
1506                files: USER,
1507            }),
1508            SettingsPageItem::SettingItem(SettingItem {
1509                title: "Excerpt Context Lines",
1510                description: "How many lines of context to provide in multibuffer excerpts by default.",
1511                field: Box::new(SettingField {
1512                    json_path: Some("excerpt_context_lines"),
1513                    pick: |settings_content| settings_content.editor.excerpt_context_lines.as_ref(),
1514                    write: |settings_content, value| {
1515                        settings_content.editor.excerpt_context_lines = value;
1516                    },
1517                }),
1518                metadata: None,
1519                files: USER,
1520            }),
1521            SettingsPageItem::SettingItem(SettingItem {
1522                title: "Expand Outlines With Depth",
1523                description: "Default depth to expand outline items in the current file.",
1524                field: Box::new(SettingField {
1525                    json_path: Some("outline_panel.expand_outlines_with_depth"),
1526                    pick: |settings_content| {
1527                        settings_content
1528                            .outline_panel
1529                            .as_ref()
1530                            .and_then(|outline_panel| {
1531                                outline_panel.expand_outlines_with_depth.as_ref()
1532                            })
1533                    },
1534                    write: |settings_content, value| {
1535                        settings_content
1536                            .outline_panel
1537                            .get_or_insert_default()
1538                            .expand_outlines_with_depth = value;
1539                    },
1540                }),
1541                metadata: None,
1542                files: USER,
1543            }),
1544            SettingsPageItem::SettingItem(SettingItem {
1545                title: "Diff View Style",
1546                description: "How to display diffs in the editor.",
1547                field: Box::new(SettingField {
1548                    json_path: Some("diff_view_style"),
1549                    pick: |settings_content| settings_content.editor.diff_view_style.as_ref(),
1550                    write: |settings_content, value| {
1551                        settings_content.editor.diff_view_style = value;
1552                    },
1553                }),
1554                metadata: None,
1555                files: USER,
1556            }),
1557            SettingsPageItem::SettingItem(SettingItem {
1558                title: "Minimum Split Diff Width",
1559                description: "The minimum width (in columns) at which the split diff view is used. When the editor is narrower, the diff view automatically switches to unified mode. Set to 0 to disable.",
1560                field: Box::new(SettingField {
1561                    json_path: Some("minimum_split_diff_width"),
1562                    pick: |settings_content| {
1563                        settings_content.editor.minimum_split_diff_width.as_ref()
1564                    },
1565                    write: |settings_content, value| {
1566                        settings_content.editor.minimum_split_diff_width = value;
1567                    },
1568                }),
1569                metadata: None,
1570                files: USER,
1571            }),
1572        ]
1573    }
1574
1575    fn scrolling_section() -> [SettingsPageItem; 8] {
1576        [
1577            SettingsPageItem::SectionHeader("Scrolling"),
1578            SettingsPageItem::SettingItem(SettingItem {
1579                title: "Scroll Beyond Last Line",
1580                description: "Whether the editor will scroll beyond the last line.",
1581                field: Box::new(SettingField {
1582                    json_path: Some("scroll_beyond_last_line"),
1583                    pick: |settings_content| {
1584                        settings_content.editor.scroll_beyond_last_line.as_ref()
1585                    },
1586                    write: |settings_content, value| {
1587                        settings_content.editor.scroll_beyond_last_line = value;
1588                    },
1589                }),
1590                metadata: None,
1591                files: USER,
1592            }),
1593            SettingsPageItem::SettingItem(SettingItem {
1594                title: "Vertical Scroll Margin",
1595                description: "The number of lines to keep above/below the cursor when auto-scrolling.",
1596                field: Box::new(SettingField {
1597                    json_path: Some("vertical_scroll_margin"),
1598                    pick: |settings_content| {
1599                        settings_content.editor.vertical_scroll_margin.as_ref()
1600                    },
1601                    write: |settings_content, value| {
1602                        settings_content.editor.vertical_scroll_margin = value;
1603                    },
1604                }),
1605                metadata: None,
1606                files: USER,
1607            }),
1608            SettingsPageItem::SettingItem(SettingItem {
1609                title: "Horizontal Scroll Margin",
1610                description: "The number of characters to keep on either side when scrolling with the mouse.",
1611                field: Box::new(SettingField {
1612                    json_path: Some("horizontal_scroll_margin"),
1613                    pick: |settings_content| {
1614                        settings_content.editor.horizontal_scroll_margin.as_ref()
1615                    },
1616                    write: |settings_content, value| {
1617                        settings_content.editor.horizontal_scroll_margin = value;
1618                    },
1619                }),
1620                metadata: None,
1621                files: USER,
1622            }),
1623            SettingsPageItem::SettingItem(SettingItem {
1624                title: "Scroll Sensitivity",
1625                description: "Scroll sensitivity multiplier for both horizontal and vertical scrolling.",
1626                field: Box::new(SettingField {
1627                    json_path: Some("scroll_sensitivity"),
1628                    pick: |settings_content| settings_content.editor.scroll_sensitivity.as_ref(),
1629                    write: |settings_content, value| {
1630                        settings_content.editor.scroll_sensitivity = value;
1631                    },
1632                }),
1633                metadata: None,
1634                files: USER,
1635            }),
1636            SettingsPageItem::SettingItem(SettingItem {
1637                title: "Fast Scroll Sensitivity",
1638                description: "Fast scroll sensitivity multiplier for both horizontal and vertical scrolling.",
1639                field: Box::new(SettingField {
1640                    json_path: Some("fast_scroll_sensitivity"),
1641                    pick: |settings_content| {
1642                        settings_content.editor.fast_scroll_sensitivity.as_ref()
1643                    },
1644                    write: |settings_content, value| {
1645                        settings_content.editor.fast_scroll_sensitivity = value;
1646                    },
1647                }),
1648                metadata: None,
1649                files: USER,
1650            }),
1651            SettingsPageItem::SettingItem(SettingItem {
1652                title: "Autoscroll On Clicks",
1653                description: "Whether to scroll when clicking near the edge of the visible text area.",
1654                field: Box::new(SettingField {
1655                    json_path: Some("autoscroll_on_clicks"),
1656                    pick: |settings_content| settings_content.editor.autoscroll_on_clicks.as_ref(),
1657                    write: |settings_content, value| {
1658                        settings_content.editor.autoscroll_on_clicks = value;
1659                    },
1660                }),
1661                metadata: None,
1662                files: USER,
1663            }),
1664            SettingsPageItem::SettingItem(SettingItem {
1665                title: "Sticky Scroll",
1666                description: "Whether to stick scopes to the top of the editor",
1667                field: Box::new(SettingField {
1668                    json_path: Some("sticky_scroll.enabled"),
1669                    pick: |settings_content| {
1670                        settings_content
1671                            .editor
1672                            .sticky_scroll
1673                            .as_ref()
1674                            .and_then(|sticky_scroll| sticky_scroll.enabled.as_ref())
1675                    },
1676                    write: |settings_content, value| {
1677                        settings_content
1678                            .editor
1679                            .sticky_scroll
1680                            .get_or_insert_default()
1681                            .enabled = value;
1682                    },
1683                }),
1684                metadata: None,
1685                files: USER,
1686            }),
1687        ]
1688    }
1689
1690    fn signature_help_section() -> [SettingsPageItem; 4] {
1691        [
1692            SettingsPageItem::SectionHeader("Signature Help"),
1693            SettingsPageItem::SettingItem(SettingItem {
1694                title: "Auto Signature Help",
1695                description: "Automatically show a signature help pop-up.",
1696                field: Box::new(SettingField {
1697                    json_path: Some("auto_signature_help"),
1698                    pick: |settings_content| settings_content.editor.auto_signature_help.as_ref(),
1699                    write: |settings_content, value| {
1700                        settings_content.editor.auto_signature_help = value;
1701                    },
1702                }),
1703                metadata: None,
1704                files: USER,
1705            }),
1706            SettingsPageItem::SettingItem(SettingItem {
1707                title: "Show Signature Help After Edits",
1708                description: "Show the signature help pop-up after completions or bracket pairs are inserted.",
1709                field: Box::new(SettingField {
1710                    json_path: Some("show_signature_help_after_edits"),
1711                    pick: |settings_content| {
1712                        settings_content
1713                            .editor
1714                            .show_signature_help_after_edits
1715                            .as_ref()
1716                    },
1717                    write: |settings_content, value| {
1718                        settings_content.editor.show_signature_help_after_edits = value;
1719                    },
1720                }),
1721                metadata: None,
1722                files: USER,
1723            }),
1724            SettingsPageItem::SettingItem(SettingItem {
1725                title: "Snippet Sort Order",
1726                description: "Determines how snippets are sorted relative to other completion items.",
1727                field: Box::new(SettingField {
1728                    json_path: Some("snippet_sort_order"),
1729                    pick: |settings_content| settings_content.editor.snippet_sort_order.as_ref(),
1730                    write: |settings_content, value| {
1731                        settings_content.editor.snippet_sort_order = value;
1732                    },
1733                }),
1734                metadata: None,
1735                files: USER,
1736            }),
1737        ]
1738    }
1739
1740    fn hover_popover_section() -> [SettingsPageItem; 3] {
1741        [
1742            SettingsPageItem::SectionHeader("Hover Popover"),
1743            SettingsPageItem::SettingItem(SettingItem {
1744                title: "Enabled",
1745                description: "Show the informational hover box when moving the mouse over symbols in the editor.",
1746                field: Box::new(SettingField {
1747                    json_path: Some("hover_popover_enabled"),
1748                    pick: |settings_content| settings_content.editor.hover_popover_enabled.as_ref(),
1749                    write: |settings_content, value| {
1750                        settings_content.editor.hover_popover_enabled = value;
1751                    },
1752                }),
1753                metadata: None,
1754                files: USER,
1755            }),
1756            // todo(settings ui): add units to this number input
1757            SettingsPageItem::SettingItem(SettingItem {
1758                title: "Delay",
1759                description: "Time to wait in milliseconds before showing the informational hover box.",
1760                field: Box::new(SettingField {
1761                    json_path: Some("hover_popover_enabled"),
1762                    pick: |settings_content| settings_content.editor.hover_popover_delay.as_ref(),
1763                    write: |settings_content, value| {
1764                        settings_content.editor.hover_popover_delay = value;
1765                    },
1766                }),
1767                metadata: None,
1768                files: USER,
1769            }),
1770        ]
1771    }
1772
1773    fn drag_and_drop_selection_section() -> [SettingsPageItem; 3] {
1774        [
1775            SettingsPageItem::SectionHeader("Drag And Drop Selection"),
1776            SettingsPageItem::SettingItem(SettingItem {
1777                title: "Enabled",
1778                description: "Enable drag and drop selection.",
1779                field: Box::new(SettingField {
1780                    json_path: Some("drag_and_drop_selection.enabled"),
1781                    pick: |settings_content| {
1782                        settings_content
1783                            .editor
1784                            .drag_and_drop_selection
1785                            .as_ref()
1786                            .and_then(|drag_and_drop| drag_and_drop.enabled.as_ref())
1787                    },
1788                    write: |settings_content, value| {
1789                        settings_content
1790                            .editor
1791                            .drag_and_drop_selection
1792                            .get_or_insert_default()
1793                            .enabled = value;
1794                    },
1795                }),
1796                metadata: None,
1797                files: USER,
1798            }),
1799            SettingsPageItem::SettingItem(SettingItem {
1800                title: "Delay",
1801                description: "Delay in milliseconds before drag and drop selection starts.",
1802                field: Box::new(SettingField {
1803                    json_path: Some("drag_and_drop_selection.delay"),
1804                    pick: |settings_content| {
1805                        settings_content
1806                            .editor
1807                            .drag_and_drop_selection
1808                            .as_ref()
1809                            .and_then(|drag_and_drop| drag_and_drop.delay.as_ref())
1810                    },
1811                    write: |settings_content, value| {
1812                        settings_content
1813                            .editor
1814                            .drag_and_drop_selection
1815                            .get_or_insert_default()
1816                            .delay = value;
1817                    },
1818                }),
1819                metadata: None,
1820                files: USER,
1821            }),
1822        ]
1823    }
1824
1825    fn gutter_section() -> [SettingsPageItem; 8] {
1826        [
1827            SettingsPageItem::SectionHeader("Gutter"),
1828            SettingsPageItem::SettingItem(SettingItem {
1829                title: "Show Line Numbers",
1830                description: "Show line numbers in the gutter.",
1831                field: Box::new(SettingField {
1832                    json_path: Some("gutter.line_numbers"),
1833                    pick: |settings_content| {
1834                        settings_content
1835                            .editor
1836                            .gutter
1837                            .as_ref()
1838                            .and_then(|gutter| gutter.line_numbers.as_ref())
1839                    },
1840                    write: |settings_content, value| {
1841                        settings_content
1842                            .editor
1843                            .gutter
1844                            .get_or_insert_default()
1845                            .line_numbers = value;
1846                    },
1847                }),
1848                metadata: None,
1849                files: USER,
1850            }),
1851            SettingsPageItem::SettingItem(SettingItem {
1852                title: "Relative Line Numbers",
1853                description: "Controls line number display in the editor's gutter. \"disabled\" shows absolute line numbers, \"enabled\" shows relative line numbers for each absolute line, and \"wrapped\" shows relative line numbers for every line, absolute or wrapped.",
1854                field: Box::new(SettingField {
1855                    json_path: Some("relative_line_numbers"),
1856                    pick: |settings_content| settings_content.editor.relative_line_numbers.as_ref(),
1857                    write: |settings_content, value| {
1858                        settings_content.editor.relative_line_numbers = value;
1859                    },
1860                }),
1861                metadata: None,
1862                files: USER,
1863            }),
1864            SettingsPageItem::SettingItem(SettingItem {
1865                title: "Show Runnables",
1866                description: "Show runnable buttons in the gutter.",
1867                field: Box::new(SettingField {
1868                    json_path: Some("gutter.runnables"),
1869                    pick: |settings_content| {
1870                        settings_content
1871                            .editor
1872                            .gutter
1873                            .as_ref()
1874                            .and_then(|gutter| gutter.runnables.as_ref())
1875                    },
1876                    write: |settings_content, value| {
1877                        settings_content
1878                            .editor
1879                            .gutter
1880                            .get_or_insert_default()
1881                            .runnables = value;
1882                    },
1883                }),
1884                metadata: None,
1885                files: USER,
1886            }),
1887            SettingsPageItem::SettingItem(SettingItem {
1888                title: "Show Breakpoints",
1889                description: "Show breakpoints in the gutter.",
1890                field: Box::new(SettingField {
1891                    json_path: Some("gutter.breakpoints"),
1892                    pick: |settings_content| {
1893                        settings_content
1894                            .editor
1895                            .gutter
1896                            .as_ref()
1897                            .and_then(|gutter| gutter.breakpoints.as_ref())
1898                    },
1899                    write: |settings_content, value| {
1900                        settings_content
1901                            .editor
1902                            .gutter
1903                            .get_or_insert_default()
1904                            .breakpoints = value;
1905                    },
1906                }),
1907                metadata: None,
1908                files: USER,
1909            }),
1910            SettingsPageItem::SettingItem(SettingItem {
1911                title: "Show Folds",
1912                description: "Show code folding controls in the gutter.",
1913                field: Box::new(SettingField {
1914                    json_path: Some("gutter.folds"),
1915                    pick: |settings_content| {
1916                        settings_content
1917                            .editor
1918                            .gutter
1919                            .as_ref()
1920                            .and_then(|gutter| gutter.folds.as_ref())
1921                    },
1922                    write: |settings_content, value| {
1923                        settings_content.editor.gutter.get_or_insert_default().folds = value;
1924                    },
1925                }),
1926                metadata: None,
1927                files: USER,
1928            }),
1929            SettingsPageItem::SettingItem(SettingItem {
1930                title: "Min Line Number Digits",
1931                description: "Minimum number of characters to reserve space for in the gutter.",
1932                field: Box::new(SettingField {
1933                    json_path: Some("gutter.min_line_number_digits"),
1934                    pick: |settings_content| {
1935                        settings_content
1936                            .editor
1937                            .gutter
1938                            .as_ref()
1939                            .and_then(|gutter| gutter.min_line_number_digits.as_ref())
1940                    },
1941                    write: |settings_content, value| {
1942                        settings_content
1943                            .editor
1944                            .gutter
1945                            .get_or_insert_default()
1946                            .min_line_number_digits = value;
1947                    },
1948                }),
1949                metadata: None,
1950                files: USER,
1951            }),
1952            SettingsPageItem::SettingItem(SettingItem {
1953                title: "Inline Code Actions",
1954                description: "Show code action button at start of buffer line.",
1955                field: Box::new(SettingField {
1956                    json_path: Some("inline_code_actions"),
1957                    pick: |settings_content| settings_content.editor.inline_code_actions.as_ref(),
1958                    write: |settings_content, value| {
1959                        settings_content.editor.inline_code_actions = value;
1960                    },
1961                }),
1962                metadata: None,
1963                files: USER,
1964            }),
1965        ]
1966    }
1967
1968    fn scrollbar_section() -> [SettingsPageItem; 10] {
1969        [
1970            SettingsPageItem::SectionHeader("Scrollbar"),
1971            SettingsPageItem::SettingItem(SettingItem {
1972                title: "Show",
1973                description: "When to show the scrollbar in the editor.",
1974                field: Box::new(SettingField {
1975                    json_path: Some("scrollbar"),
1976                    pick: |settings_content| {
1977                        settings_content.editor.scrollbar.as_ref()?.show.as_ref()
1978                    },
1979                    write: |settings_content, value| {
1980                        settings_content
1981                            .editor
1982                            .scrollbar
1983                            .get_or_insert_default()
1984                            .show = value;
1985                    },
1986                }),
1987                metadata: None,
1988                files: USER,
1989            }),
1990            SettingsPageItem::SettingItem(SettingItem {
1991                title: "Cursors",
1992                description: "Show cursor positions in the scrollbar.",
1993                field: Box::new(SettingField {
1994                    json_path: Some("scrollbar.cursors"),
1995                    pick: |settings_content| {
1996                        settings_content.editor.scrollbar.as_ref()?.cursors.as_ref()
1997                    },
1998                    write: |settings_content, value| {
1999                        settings_content
2000                            .editor
2001                            .scrollbar
2002                            .get_or_insert_default()
2003                            .cursors = value;
2004                    },
2005                }),
2006                metadata: None,
2007                files: USER,
2008            }),
2009            SettingsPageItem::SettingItem(SettingItem {
2010                title: "Git Diff",
2011                description: "Show Git diff indicators in the scrollbar.",
2012                field: Box::new(SettingField {
2013                    json_path: Some("scrollbar.git_diff"),
2014                    pick: |settings_content| {
2015                        settings_content
2016                            .editor
2017                            .scrollbar
2018                            .as_ref()?
2019                            .git_diff
2020                            .as_ref()
2021                    },
2022                    write: |settings_content, value| {
2023                        settings_content
2024                            .editor
2025                            .scrollbar
2026                            .get_or_insert_default()
2027                            .git_diff = value;
2028                    },
2029                }),
2030                metadata: None,
2031                files: USER,
2032            }),
2033            SettingsPageItem::SettingItem(SettingItem {
2034                title: "Search Results",
2035                description: "Show buffer search result indicators in the scrollbar.",
2036                field: Box::new(SettingField {
2037                    json_path: Some("scrollbar.search_results"),
2038                    pick: |settings_content| {
2039                        settings_content
2040                            .editor
2041                            .scrollbar
2042                            .as_ref()?
2043                            .search_results
2044                            .as_ref()
2045                    },
2046                    write: |settings_content, value| {
2047                        settings_content
2048                            .editor
2049                            .scrollbar
2050                            .get_or_insert_default()
2051                            .search_results = value;
2052                    },
2053                }),
2054                metadata: None,
2055                files: USER,
2056            }),
2057            SettingsPageItem::SettingItem(SettingItem {
2058                title: "Selected Text",
2059                description: "Show selected text occurrences in the scrollbar.",
2060                field: Box::new(SettingField {
2061                    json_path: Some("scrollbar.selected_text"),
2062                    pick: |settings_content| {
2063                        settings_content
2064                            .editor
2065                            .scrollbar
2066                            .as_ref()?
2067                            .selected_text
2068                            .as_ref()
2069                    },
2070                    write: |settings_content, value| {
2071                        settings_content
2072                            .editor
2073                            .scrollbar
2074                            .get_or_insert_default()
2075                            .selected_text = value;
2076                    },
2077                }),
2078                metadata: None,
2079                files: USER,
2080            }),
2081            SettingsPageItem::SettingItem(SettingItem {
2082                title: "Selected Symbol",
2083                description: "Show selected symbol occurrences in the scrollbar.",
2084                field: Box::new(SettingField {
2085                    json_path: Some("scrollbar.selected_symbol"),
2086                    pick: |settings_content| {
2087                        settings_content
2088                            .editor
2089                            .scrollbar
2090                            .as_ref()?
2091                            .selected_symbol
2092                            .as_ref()
2093                    },
2094                    write: |settings_content, value| {
2095                        settings_content
2096                            .editor
2097                            .scrollbar
2098                            .get_or_insert_default()
2099                            .selected_symbol = value;
2100                    },
2101                }),
2102                metadata: None,
2103                files: USER,
2104            }),
2105            SettingsPageItem::SettingItem(SettingItem {
2106                title: "Diagnostics",
2107                description: "Which diagnostic indicators to show in the scrollbar.",
2108                field: Box::new(SettingField {
2109                    json_path: Some("scrollbar.diagnostics"),
2110                    pick: |settings_content| {
2111                        settings_content
2112                            .editor
2113                            .scrollbar
2114                            .as_ref()?
2115                            .diagnostics
2116                            .as_ref()
2117                    },
2118                    write: |settings_content, value| {
2119                        settings_content
2120                            .editor
2121                            .scrollbar
2122                            .get_or_insert_default()
2123                            .diagnostics = value;
2124                    },
2125                }),
2126                metadata: None,
2127                files: USER,
2128            }),
2129            SettingsPageItem::SettingItem(SettingItem {
2130                title: "Horizontal Scrollbar",
2131                description: "When false, forcefully disables the horizontal scrollbar.",
2132                field: Box::new(SettingField {
2133                    json_path: Some("scrollbar.axes.horizontal"),
2134                    pick: |settings_content| {
2135                        settings_content
2136                            .editor
2137                            .scrollbar
2138                            .as_ref()?
2139                            .axes
2140                            .as_ref()?
2141                            .horizontal
2142                            .as_ref()
2143                    },
2144                    write: |settings_content, value| {
2145                        settings_content
2146                            .editor
2147                            .scrollbar
2148                            .get_or_insert_default()
2149                            .axes
2150                            .get_or_insert_default()
2151                            .horizontal = value;
2152                    },
2153                }),
2154                metadata: None,
2155                files: USER,
2156            }),
2157            SettingsPageItem::SettingItem(SettingItem {
2158                title: "Vertical Scrollbar",
2159                description: "When false, forcefully disables the vertical scrollbar.",
2160                field: Box::new(SettingField {
2161                    json_path: Some("scrollbar.axes.vertical"),
2162                    pick: |settings_content| {
2163                        settings_content
2164                            .editor
2165                            .scrollbar
2166                            .as_ref()?
2167                            .axes
2168                            .as_ref()?
2169                            .vertical
2170                            .as_ref()
2171                    },
2172                    write: |settings_content, value| {
2173                        settings_content
2174                            .editor
2175                            .scrollbar
2176                            .get_or_insert_default()
2177                            .axes
2178                            .get_or_insert_default()
2179                            .vertical = value;
2180                    },
2181                }),
2182                metadata: None,
2183                files: USER,
2184            }),
2185        ]
2186    }
2187
2188    fn minimap_section() -> [SettingsPageItem; 7] {
2189        [
2190            SettingsPageItem::SectionHeader("Minimap"),
2191            SettingsPageItem::SettingItem(SettingItem {
2192                title: "Show",
2193                description: "When to show the minimap in the editor.",
2194                field: Box::new(SettingField {
2195                    json_path: Some("minimap.show"),
2196                    pick: |settings_content| {
2197                        settings_content.editor.minimap.as_ref()?.show.as_ref()
2198                    },
2199                    write: |settings_content, value| {
2200                        settings_content.editor.minimap.get_or_insert_default().show = value;
2201                    },
2202                }),
2203                metadata: None,
2204                files: USER,
2205            }),
2206            SettingsPageItem::SettingItem(SettingItem {
2207                title: "Display In",
2208                description: "Where to show the minimap in the editor.",
2209                field: Box::new(SettingField {
2210                    json_path: Some("minimap.display_in"),
2211                    pick: |settings_content| {
2212                        settings_content
2213                            .editor
2214                            .minimap
2215                            .as_ref()?
2216                            .display_in
2217                            .as_ref()
2218                    },
2219                    write: |settings_content, value| {
2220                        settings_content
2221                            .editor
2222                            .minimap
2223                            .get_or_insert_default()
2224                            .display_in = value;
2225                    },
2226                }),
2227                metadata: None,
2228                files: USER,
2229            }),
2230            SettingsPageItem::SettingItem(SettingItem {
2231                title: "Thumb",
2232                description: "When to show the minimap thumb.",
2233                field: Box::new(SettingField {
2234                    json_path: Some("minimap.thumb"),
2235                    pick: |settings_content| {
2236                        settings_content.editor.minimap.as_ref()?.thumb.as_ref()
2237                    },
2238                    write: |settings_content, value| {
2239                        settings_content
2240                            .editor
2241                            .minimap
2242                            .get_or_insert_default()
2243                            .thumb = value;
2244                    },
2245                }),
2246                metadata: None,
2247                files: USER,
2248            }),
2249            SettingsPageItem::SettingItem(SettingItem {
2250                title: "Thumb Border",
2251                description: "Border style for the minimap's scrollbar thumb.",
2252                field: Box::new(SettingField {
2253                    json_path: Some("minimap.thumb_border"),
2254                    pick: |settings_content| {
2255                        settings_content
2256                            .editor
2257                            .minimap
2258                            .as_ref()?
2259                            .thumb_border
2260                            .as_ref()
2261                    },
2262                    write: |settings_content, value| {
2263                        settings_content
2264                            .editor
2265                            .minimap
2266                            .get_or_insert_default()
2267                            .thumb_border = value;
2268                    },
2269                }),
2270                metadata: None,
2271                files: USER,
2272            }),
2273            SettingsPageItem::SettingItem(SettingItem {
2274                title: "Current Line Highlight",
2275                description: "How to highlight the current line in the minimap.",
2276                field: Box::new(SettingField {
2277                    json_path: Some("minimap.current_line_highlight"),
2278                    pick: |settings_content| {
2279                        settings_content
2280                            .editor
2281                            .minimap
2282                            .as_ref()
2283                            .and_then(|minimap| minimap.current_line_highlight.as_ref())
2284                            .or(settings_content.editor.current_line_highlight.as_ref())
2285                    },
2286                    write: |settings_content, value| {
2287                        settings_content
2288                            .editor
2289                            .minimap
2290                            .get_or_insert_default()
2291                            .current_line_highlight = value;
2292                    },
2293                }),
2294                metadata: None,
2295                files: USER,
2296            }),
2297            SettingsPageItem::SettingItem(SettingItem {
2298                title: "Max Width Columns",
2299                description: "Maximum number of columns to display in the minimap.",
2300                field: Box::new(SettingField {
2301                    json_path: Some("minimap.max_width_columns"),
2302                    pick: |settings_content| {
2303                        settings_content
2304                            .editor
2305                            .minimap
2306                            .as_ref()?
2307                            .max_width_columns
2308                            .as_ref()
2309                    },
2310                    write: |settings_content, value| {
2311                        settings_content
2312                            .editor
2313                            .minimap
2314                            .get_or_insert_default()
2315                            .max_width_columns = value;
2316                    },
2317                }),
2318                metadata: None,
2319                files: USER,
2320            }),
2321        ]
2322    }
2323
2324    fn toolbar_section() -> [SettingsPageItem; 6] {
2325        [
2326            SettingsPageItem::SectionHeader("Toolbar"),
2327            SettingsPageItem::SettingItem(SettingItem {
2328                title: "Breadcrumbs",
2329                description: "Show breadcrumbs.",
2330                field: Box::new(SettingField {
2331                    json_path: Some("toolbar.breadcrumbs"),
2332                    pick: |settings_content| {
2333                        settings_content
2334                            .editor
2335                            .toolbar
2336                            .as_ref()?
2337                            .breadcrumbs
2338                            .as_ref()
2339                    },
2340                    write: |settings_content, value| {
2341                        settings_content
2342                            .editor
2343                            .toolbar
2344                            .get_or_insert_default()
2345                            .breadcrumbs = value;
2346                    },
2347                }),
2348                metadata: None,
2349                files: USER,
2350            }),
2351            SettingsPageItem::SettingItem(SettingItem {
2352                title: "Quick Actions",
2353                description: "Show quick action buttons (e.g., search, selection, editor controls, etc.).",
2354                field: Box::new(SettingField {
2355                    json_path: Some("toolbar.quick_actions"),
2356                    pick: |settings_content| {
2357                        settings_content
2358                            .editor
2359                            .toolbar
2360                            .as_ref()?
2361                            .quick_actions
2362                            .as_ref()
2363                    },
2364                    write: |settings_content, value| {
2365                        settings_content
2366                            .editor
2367                            .toolbar
2368                            .get_or_insert_default()
2369                            .quick_actions = value;
2370                    },
2371                }),
2372                metadata: None,
2373                files: USER,
2374            }),
2375            SettingsPageItem::SettingItem(SettingItem {
2376                title: "Selections Menu",
2377                description: "Show the selections menu in the editor toolbar.",
2378                field: Box::new(SettingField {
2379                    json_path: Some("toolbar.selections_menu"),
2380                    pick: |settings_content| {
2381                        settings_content
2382                            .editor
2383                            .toolbar
2384                            .as_ref()?
2385                            .selections_menu
2386                            .as_ref()
2387                    },
2388                    write: |settings_content, value| {
2389                        settings_content
2390                            .editor
2391                            .toolbar
2392                            .get_or_insert_default()
2393                            .selections_menu = value;
2394                    },
2395                }),
2396                metadata: None,
2397                files: USER,
2398            }),
2399            SettingsPageItem::SettingItem(SettingItem {
2400                title: "Agent Review",
2401                description: "Show agent review buttons in the editor toolbar.",
2402                field: Box::new(SettingField {
2403                    json_path: Some("toolbar.agent_review"),
2404                    pick: |settings_content| {
2405                        settings_content
2406                            .editor
2407                            .toolbar
2408                            .as_ref()?
2409                            .agent_review
2410                            .as_ref()
2411                    },
2412                    write: |settings_content, value| {
2413                        settings_content
2414                            .editor
2415                            .toolbar
2416                            .get_or_insert_default()
2417                            .agent_review = value;
2418                    },
2419                }),
2420                metadata: None,
2421                files: USER,
2422            }),
2423            SettingsPageItem::SettingItem(SettingItem {
2424                title: "Code Actions",
2425                description: "Show code action buttons in the editor toolbar.",
2426                field: Box::new(SettingField {
2427                    json_path: Some("toolbar.code_actions"),
2428                    pick: |settings_content| {
2429                        settings_content
2430                            .editor
2431                            .toolbar
2432                            .as_ref()?
2433                            .code_actions
2434                            .as_ref()
2435                    },
2436                    write: |settings_content, value| {
2437                        settings_content
2438                            .editor
2439                            .toolbar
2440                            .get_or_insert_default()
2441                            .code_actions = value;
2442                    },
2443                }),
2444                metadata: None,
2445                files: USER,
2446            }),
2447        ]
2448    }
2449
2450    fn vim_settings_section() -> [SettingsPageItem; 12] {
2451        [
2452            SettingsPageItem::SectionHeader("Vim"),
2453            SettingsPageItem::SettingItem(SettingItem {
2454                title: "Default Mode",
2455                description: "The default mode when Vim starts.",
2456                field: Box::new(SettingField {
2457                    json_path: Some("vim.default_mode"),
2458                    pick: |settings_content| settings_content.vim.as_ref()?.default_mode.as_ref(),
2459                    write: |settings_content, value| {
2460                        settings_content.vim.get_or_insert_default().default_mode = value;
2461                    },
2462                }),
2463                metadata: None,
2464                files: USER,
2465            }),
2466            SettingsPageItem::SettingItem(SettingItem {
2467                title: "Toggle Relative Line Numbers",
2468                description: "Toggle relative line numbers in Vim mode.",
2469                field: Box::new(SettingField {
2470                    json_path: Some("vim.toggle_relative_line_numbers"),
2471                    pick: |settings_content| {
2472                        settings_content
2473                            .vim
2474                            .as_ref()?
2475                            .toggle_relative_line_numbers
2476                            .as_ref()
2477                    },
2478                    write: |settings_content, value| {
2479                        settings_content
2480                            .vim
2481                            .get_or_insert_default()
2482                            .toggle_relative_line_numbers = value;
2483                    },
2484                }),
2485                metadata: None,
2486                files: USER,
2487            }),
2488            SettingsPageItem::SettingItem(SettingItem {
2489                title: "Use System Clipboard",
2490                description: "Controls when to use system clipboard in Vim mode.",
2491                field: Box::new(SettingField {
2492                    json_path: Some("vim.use_system_clipboard"),
2493                    pick: |settings_content| {
2494                        settings_content.vim.as_ref()?.use_system_clipboard.as_ref()
2495                    },
2496                    write: |settings_content, value| {
2497                        settings_content
2498                            .vim
2499                            .get_or_insert_default()
2500                            .use_system_clipboard = value;
2501                    },
2502                }),
2503                metadata: None,
2504                files: USER,
2505            }),
2506            SettingsPageItem::SettingItem(SettingItem {
2507                title: "Use Smartcase Find",
2508                description: "Enable smartcase searching in Vim mode.",
2509                field: Box::new(SettingField {
2510                    json_path: Some("vim.use_smartcase_find"),
2511                    pick: |settings_content| {
2512                        settings_content.vim.as_ref()?.use_smartcase_find.as_ref()
2513                    },
2514                    write: |settings_content, value| {
2515                        settings_content
2516                            .vim
2517                            .get_or_insert_default()
2518                            .use_smartcase_find = value;
2519                    },
2520                }),
2521                metadata: None,
2522                files: USER,
2523            }),
2524            SettingsPageItem::SettingItem(SettingItem {
2525                title: "Global Substitution Default",
2526                description: "When enabled, the :substitute command replaces all matches in a line by default. The 'g' flag then toggles this behavior.",
2527                field: Box::new(SettingField {
2528                    json_path: Some("vim.gdefault"),
2529                    pick: |settings_content| settings_content.vim.as_ref()?.gdefault.as_ref(),
2530                    write: |settings_content, value| {
2531                        settings_content.vim.get_or_insert_default().gdefault = value;
2532                    },
2533                }),
2534                metadata: None,
2535                files: USER,
2536            }),
2537            SettingsPageItem::SettingItem(SettingItem {
2538                title: "Highlight on Yank Duration",
2539                description: "Duration in milliseconds to highlight yanked text in Vim mode.",
2540                field: Box::new(SettingField {
2541                    json_path: Some("vim.highlight_on_yank_duration"),
2542                    pick: |settings_content| {
2543                        settings_content
2544                            .vim
2545                            .as_ref()?
2546                            .highlight_on_yank_duration
2547                            .as_ref()
2548                    },
2549                    write: |settings_content, value| {
2550                        settings_content
2551                            .vim
2552                            .get_or_insert_default()
2553                            .highlight_on_yank_duration = value;
2554                    },
2555                }),
2556                metadata: None,
2557                files: USER,
2558            }),
2559            SettingsPageItem::SettingItem(SettingItem {
2560                title: "Cursor Shape - Normal Mode",
2561                description: "Cursor shape for normal mode.",
2562                field: Box::new(SettingField {
2563                    json_path: Some("vim.cursor_shape.normal"),
2564                    pick: |settings_content| {
2565                        settings_content
2566                            .vim
2567                            .as_ref()?
2568                            .cursor_shape
2569                            .as_ref()?
2570                            .normal
2571                            .as_ref()
2572                    },
2573                    write: |settings_content, value| {
2574                        settings_content
2575                            .vim
2576                            .get_or_insert_default()
2577                            .cursor_shape
2578                            .get_or_insert_default()
2579                            .normal = value;
2580                    },
2581                }),
2582                metadata: None,
2583                files: USER,
2584            }),
2585            SettingsPageItem::SettingItem(SettingItem {
2586                title: "Cursor Shape - Insert Mode",
2587                description: "Cursor shape for insert mode. Inherit uses the editor's cursor shape.",
2588                field: Box::new(SettingField {
2589                    json_path: Some("vim.cursor_shape.insert"),
2590                    pick: |settings_content| {
2591                        settings_content
2592                            .vim
2593                            .as_ref()?
2594                            .cursor_shape
2595                            .as_ref()?
2596                            .insert
2597                            .as_ref()
2598                    },
2599                    write: |settings_content, value| {
2600                        settings_content
2601                            .vim
2602                            .get_or_insert_default()
2603                            .cursor_shape
2604                            .get_or_insert_default()
2605                            .insert = value;
2606                    },
2607                }),
2608                metadata: None,
2609                files: USER,
2610            }),
2611            SettingsPageItem::SettingItem(SettingItem {
2612                title: "Cursor Shape - Replace Mode",
2613                description: "Cursor shape for replace mode.",
2614                field: Box::new(SettingField {
2615                    json_path: Some("vim.cursor_shape.replace"),
2616                    pick: |settings_content| {
2617                        settings_content
2618                            .vim
2619                            .as_ref()?
2620                            .cursor_shape
2621                            .as_ref()?
2622                            .replace
2623                            .as_ref()
2624                    },
2625                    write: |settings_content, value| {
2626                        settings_content
2627                            .vim
2628                            .get_or_insert_default()
2629                            .cursor_shape
2630                            .get_or_insert_default()
2631                            .replace = value;
2632                    },
2633                }),
2634                metadata: None,
2635                files: USER,
2636            }),
2637            SettingsPageItem::SettingItem(SettingItem {
2638                title: "Cursor Shape - Visual Mode",
2639                description: "Cursor shape for visual mode.",
2640                field: Box::new(SettingField {
2641                    json_path: Some("vim.cursor_shape.visual"),
2642                    pick: |settings_content| {
2643                        settings_content
2644                            .vim
2645                            .as_ref()?
2646                            .cursor_shape
2647                            .as_ref()?
2648                            .visual
2649                            .as_ref()
2650                    },
2651                    write: |settings_content, value| {
2652                        settings_content
2653                            .vim
2654                            .get_or_insert_default()
2655                            .cursor_shape
2656                            .get_or_insert_default()
2657                            .visual = value;
2658                    },
2659                }),
2660                metadata: None,
2661                files: USER,
2662            }),
2663            SettingsPageItem::SettingItem(SettingItem {
2664                title: "Custom Digraphs",
2665                description: "Custom digraph mappings for Vim mode.",
2666                field: Box::new(
2667                    SettingField {
2668                        json_path: Some("vim.custom_digraphs"),
2669                        pick: |settings_content| {
2670                            settings_content.vim.as_ref()?.custom_digraphs.as_ref()
2671                        },
2672                        write: |settings_content, value| {
2673                            settings_content.vim.get_or_insert_default().custom_digraphs = value;
2674                        },
2675                    }
2676                    .unimplemented(),
2677                ),
2678                metadata: None,
2679                files: USER,
2680            }),
2681        ]
2682    }
2683
2684    let items = concat_sections!(
2685        auto_save_section(),
2686        which_key_section(),
2687        multibuffer_section(),
2688        scrolling_section(),
2689        signature_help_section(),
2690        hover_popover_section(),
2691        drag_and_drop_selection_section(),
2692        gutter_section(),
2693        scrollbar_section(),
2694        minimap_section(),
2695        toolbar_section(),
2696        vim_settings_section(),
2697        language_settings_data(),
2698    );
2699
2700    SettingsPage {
2701        title: "Editor",
2702        items: items,
2703    }
2704}
2705
2706fn languages_and_tools_page(cx: &App) -> SettingsPage {
2707    fn file_types_section() -> [SettingsPageItem; 2] {
2708        [
2709            SettingsPageItem::SectionHeader("File Types"),
2710            SettingsPageItem::SettingItem(SettingItem {
2711                title: "File Type Associations",
2712                description: "A mapping from languages to files and file extensions that should be treated as that language.",
2713                field: Box::new(
2714                    SettingField {
2715                        json_path: Some("file_type_associations"),
2716                        pick: |settings_content| {
2717                            settings_content.project.all_languages.file_types.as_ref()
2718                        },
2719                        write: |settings_content, value| {
2720                            settings_content.project.all_languages.file_types = value;
2721                        },
2722                    }
2723                    .unimplemented(),
2724                ),
2725                metadata: None,
2726                files: USER | PROJECT,
2727            }),
2728        ]
2729    }
2730
2731    fn diagnostics_section() -> [SettingsPageItem; 3] {
2732        [
2733            SettingsPageItem::SectionHeader("Diagnostics"),
2734            SettingsPageItem::SettingItem(SettingItem {
2735                title: "Max Severity",
2736                description: "Which level to use to filter out diagnostics displayed in the editor.",
2737                field: Box::new(SettingField {
2738                    json_path: Some("diagnostics_max_severity"),
2739                    pick: |settings_content| {
2740                        settings_content.editor.diagnostics_max_severity.as_ref()
2741                    },
2742                    write: |settings_content, value| {
2743                        settings_content.editor.diagnostics_max_severity = value;
2744                    },
2745                }),
2746                metadata: None,
2747                files: USER,
2748            }),
2749            SettingsPageItem::SettingItem(SettingItem {
2750                title: "Include Warnings",
2751                description: "Whether to show warnings or not by default.",
2752                field: Box::new(SettingField {
2753                    json_path: Some("diagnostics.include_warnings"),
2754                    pick: |settings_content| {
2755                        settings_content
2756                            .diagnostics
2757                            .as_ref()?
2758                            .include_warnings
2759                            .as_ref()
2760                    },
2761                    write: |settings_content, value| {
2762                        settings_content
2763                            .diagnostics
2764                            .get_or_insert_default()
2765                            .include_warnings = value;
2766                    },
2767                }),
2768                metadata: None,
2769                files: USER,
2770            }),
2771        ]
2772    }
2773
2774    fn inline_diagnostics_section() -> [SettingsPageItem; 5] {
2775        [
2776            SettingsPageItem::SectionHeader("Inline Diagnostics"),
2777            SettingsPageItem::SettingItem(SettingItem {
2778                title: "Enabled",
2779                description: "Whether to show diagnostics inline or not.",
2780                field: Box::new(SettingField {
2781                    json_path: Some("diagnostics.inline.enabled"),
2782                    pick: |settings_content| {
2783                        settings_content
2784                            .diagnostics
2785                            .as_ref()?
2786                            .inline
2787                            .as_ref()?
2788                            .enabled
2789                            .as_ref()
2790                    },
2791                    write: |settings_content, value| {
2792                        settings_content
2793                            .diagnostics
2794                            .get_or_insert_default()
2795                            .inline
2796                            .get_or_insert_default()
2797                            .enabled = value;
2798                    },
2799                }),
2800                metadata: None,
2801                files: USER,
2802            }),
2803            SettingsPageItem::SettingItem(SettingItem {
2804                title: "Update Debounce",
2805                description: "The delay in milliseconds to show inline diagnostics after the last diagnostic update.",
2806                field: Box::new(SettingField {
2807                    json_path: Some("diagnostics.inline.update_debounce_ms"),
2808                    pick: |settings_content| {
2809                        settings_content
2810                            .diagnostics
2811                            .as_ref()?
2812                            .inline
2813                            .as_ref()?
2814                            .update_debounce_ms
2815                            .as_ref()
2816                    },
2817                    write: |settings_content, value| {
2818                        settings_content
2819                            .diagnostics
2820                            .get_or_insert_default()
2821                            .inline
2822                            .get_or_insert_default()
2823                            .update_debounce_ms = value;
2824                    },
2825                }),
2826                metadata: None,
2827                files: USER,
2828            }),
2829            SettingsPageItem::SettingItem(SettingItem {
2830                title: "Padding",
2831                description: "The amount of padding between the end of the source line and the start of the inline diagnostic.",
2832                field: Box::new(SettingField {
2833                    json_path: Some("diagnostics.inline.padding"),
2834                    pick: |settings_content| {
2835                        settings_content
2836                            .diagnostics
2837                            .as_ref()?
2838                            .inline
2839                            .as_ref()?
2840                            .padding
2841                            .as_ref()
2842                    },
2843                    write: |settings_content, value| {
2844                        settings_content
2845                            .diagnostics
2846                            .get_or_insert_default()
2847                            .inline
2848                            .get_or_insert_default()
2849                            .padding = value;
2850                    },
2851                }),
2852                metadata: None,
2853                files: USER,
2854            }),
2855            SettingsPageItem::SettingItem(SettingItem {
2856                title: "Minimum Column",
2857                description: "The minimum column at which to display inline diagnostics.",
2858                field: Box::new(SettingField {
2859                    json_path: Some("diagnostics.inline.min_column"),
2860                    pick: |settings_content| {
2861                        settings_content
2862                            .diagnostics
2863                            .as_ref()?
2864                            .inline
2865                            .as_ref()?
2866                            .min_column
2867                            .as_ref()
2868                    },
2869                    write: |settings_content, value| {
2870                        settings_content
2871                            .diagnostics
2872                            .get_or_insert_default()
2873                            .inline
2874                            .get_or_insert_default()
2875                            .min_column = value;
2876                    },
2877                }),
2878                metadata: None,
2879                files: USER,
2880            }),
2881        ]
2882    }
2883
2884    fn lsp_pull_diagnostics_section() -> [SettingsPageItem; 3] {
2885        [
2886            SettingsPageItem::SectionHeader("LSP Pull Diagnostics"),
2887            SettingsPageItem::SettingItem(SettingItem {
2888                title: "Enabled",
2889                description: "Whether to pull for language server-powered diagnostics or not.",
2890                field: Box::new(SettingField {
2891                    json_path: Some("diagnostics.lsp_pull_diagnostics.enabled"),
2892                    pick: |settings_content| {
2893                        settings_content
2894                            .diagnostics
2895                            .as_ref()?
2896                            .lsp_pull_diagnostics
2897                            .as_ref()?
2898                            .enabled
2899                            .as_ref()
2900                    },
2901                    write: |settings_content, value| {
2902                        settings_content
2903                            .diagnostics
2904                            .get_or_insert_default()
2905                            .lsp_pull_diagnostics
2906                            .get_or_insert_default()
2907                            .enabled = value;
2908                    },
2909                }),
2910                metadata: None,
2911                files: USER,
2912            }),
2913            // todo(settings_ui): Needs unit
2914            SettingsPageItem::SettingItem(SettingItem {
2915                title: "Debounce",
2916                description: "Minimum time to wait before pulling diagnostics from the language server(s).",
2917                field: Box::new(SettingField {
2918                    json_path: Some("diagnostics.lsp_pull_diagnostics.debounce_ms"),
2919                    pick: |settings_content| {
2920                        settings_content
2921                            .diagnostics
2922                            .as_ref()?
2923                            .lsp_pull_diagnostics
2924                            .as_ref()?
2925                            .debounce_ms
2926                            .as_ref()
2927                    },
2928                    write: |settings_content, value| {
2929                        settings_content
2930                            .diagnostics
2931                            .get_or_insert_default()
2932                            .lsp_pull_diagnostics
2933                            .get_or_insert_default()
2934                            .debounce_ms = value;
2935                    },
2936                }),
2937                metadata: None,
2938                files: USER,
2939            }),
2940        ]
2941    }
2942
2943    fn lsp_highlights_section() -> [SettingsPageItem; 2] {
2944        [
2945            SettingsPageItem::SectionHeader("LSP Highlights"),
2946            SettingsPageItem::SettingItem(SettingItem {
2947                title: "Debounce",
2948                description: "The debounce delay before querying highlights from the language.",
2949                field: Box::new(SettingField {
2950                    json_path: Some("lsp_highlight_debounce"),
2951                    pick: |settings_content| {
2952                        settings_content.editor.lsp_highlight_debounce.as_ref()
2953                    },
2954                    write: |settings_content, value| {
2955                        settings_content.editor.lsp_highlight_debounce = value;
2956                    },
2957                }),
2958                metadata: None,
2959                files: USER,
2960            }),
2961        ]
2962    }
2963
2964    fn languages_list_section(cx: &App) -> Box<[SettingsPageItem]> {
2965        // todo(settings_ui): Refresh on extension (un)/installed
2966        // Note that `crates/json_schema_store` solves the same problem, there is probably a way to unify the two
2967        std::iter::once(SettingsPageItem::SectionHeader("Languages"))
2968            .chain(all_language_names(cx).into_iter().map(|language_name| {
2969                let link = format!("languages.{language_name}");
2970                SettingsPageItem::SubPageLink(SubPageLink {
2971                    title: language_name,
2972                    r#type: crate::SubPageType::Language,
2973                    description: None,
2974                    json_path: Some(link.leak()),
2975                    in_json: true,
2976                    files: USER | PROJECT,
2977                    render: |this, scroll_handle, window, cx| {
2978                        let items: Box<[SettingsPageItem]> = concat_sections!(
2979                            language_settings_data(),
2980                            non_editor_language_settings_data(),
2981                            edit_prediction_language_settings_section()
2982                        );
2983                        this.render_sub_page_items(
2984                            items.iter().enumerate(),
2985                            scroll_handle,
2986                            window,
2987                            cx,
2988                        )
2989                        .into_any_element()
2990                    },
2991                })
2992            }))
2993            .collect()
2994    }
2995
2996    SettingsPage {
2997        title: "Languages & Tools",
2998        items: {
2999            concat_sections!(
3000                non_editor_language_settings_data(),
3001                file_types_section(),
3002                diagnostics_section(),
3003                inline_diagnostics_section(),
3004                lsp_pull_diagnostics_section(),
3005                lsp_highlights_section(),
3006                languages_list_section(cx),
3007            )
3008        },
3009    }
3010}
3011
3012fn search_and_files_page() -> SettingsPage {
3013    fn search_section() -> [SettingsPageItem; 9] {
3014        [
3015            SettingsPageItem::SectionHeader("Search"),
3016            SettingsPageItem::SettingItem(SettingItem {
3017                title: "Whole Word",
3018                description: "Search for whole words by default.",
3019                field: Box::new(SettingField {
3020                    json_path: Some("search.whole_word"),
3021                    pick: |settings_content| {
3022                        settings_content.editor.search.as_ref()?.whole_word.as_ref()
3023                    },
3024                    write: |settings_content, value| {
3025                        settings_content
3026                            .editor
3027                            .search
3028                            .get_or_insert_default()
3029                            .whole_word = value;
3030                    },
3031                }),
3032                metadata: None,
3033                files: USER,
3034            }),
3035            SettingsPageItem::SettingItem(SettingItem {
3036                title: "Case Sensitive",
3037                description: "Search case-sensitively by default.",
3038                field: Box::new(SettingField {
3039                    json_path: Some("search.case_sensitive"),
3040                    pick: |settings_content| {
3041                        settings_content
3042                            .editor
3043                            .search
3044                            .as_ref()?
3045                            .case_sensitive
3046                            .as_ref()
3047                    },
3048                    write: |settings_content, value| {
3049                        settings_content
3050                            .editor
3051                            .search
3052                            .get_or_insert_default()
3053                            .case_sensitive = value;
3054                    },
3055                }),
3056                metadata: None,
3057                files: USER,
3058            }),
3059            SettingsPageItem::SettingItem(SettingItem {
3060                title: "Use Smartcase Search",
3061                description: "Whether to automatically enable case-sensitive search based on the search query.",
3062                field: Box::new(SettingField {
3063                    json_path: Some("use_smartcase_search"),
3064                    pick: |settings_content| settings_content.editor.use_smartcase_search.as_ref(),
3065                    write: |settings_content, value| {
3066                        settings_content.editor.use_smartcase_search = value;
3067                    },
3068                }),
3069                metadata: None,
3070                files: USER,
3071            }),
3072            SettingsPageItem::SettingItem(SettingItem {
3073                title: "Include Ignored",
3074                description: "Include ignored files in search results by default.",
3075                field: Box::new(SettingField {
3076                    json_path: Some("search.include_ignored"),
3077                    pick: |settings_content| {
3078                        settings_content
3079                            .editor
3080                            .search
3081                            .as_ref()?
3082                            .include_ignored
3083                            .as_ref()
3084                    },
3085                    write: |settings_content, value| {
3086                        settings_content
3087                            .editor
3088                            .search
3089                            .get_or_insert_default()
3090                            .include_ignored = value;
3091                    },
3092                }),
3093                metadata: None,
3094                files: USER,
3095            }),
3096            SettingsPageItem::SettingItem(SettingItem {
3097                title: "Regex",
3098                description: "Use regex search by default.",
3099                field: Box::new(SettingField {
3100                    json_path: Some("search.regex"),
3101                    pick: |settings_content| {
3102                        settings_content.editor.search.as_ref()?.regex.as_ref()
3103                    },
3104                    write: |settings_content, value| {
3105                        settings_content.editor.search.get_or_insert_default().regex = value;
3106                    },
3107                }),
3108                metadata: None,
3109                files: USER,
3110            }),
3111            SettingsPageItem::SettingItem(SettingItem {
3112                title: "Search Wrap",
3113                description: "Whether the editor search results will loop.",
3114                field: Box::new(SettingField {
3115                    json_path: Some("search_wrap"),
3116                    pick: |settings_content| settings_content.editor.search_wrap.as_ref(),
3117                    write: |settings_content, value| {
3118                        settings_content.editor.search_wrap = value;
3119                    },
3120                }),
3121                metadata: None,
3122                files: USER,
3123            }),
3124            SettingsPageItem::SettingItem(SettingItem {
3125                title: "Center on Match",
3126                description: "Whether to center the current match in the editor",
3127                field: Box::new(SettingField {
3128                    json_path: Some("editor.search.center_on_match"),
3129                    pick: |settings_content| {
3130                        settings_content
3131                            .editor
3132                            .search
3133                            .as_ref()
3134                            .and_then(|search| search.center_on_match.as_ref())
3135                    },
3136                    write: |settings_content, value| {
3137                        settings_content
3138                            .editor
3139                            .search
3140                            .get_or_insert_default()
3141                            .center_on_match = value;
3142                    },
3143                }),
3144                metadata: None,
3145                files: USER,
3146            }),
3147            SettingsPageItem::SettingItem(SettingItem {
3148                title: "Seed Search Query From Cursor",
3149                description: "When to populate a new search's query based on the text under the cursor.",
3150                field: Box::new(SettingField {
3151                    json_path: Some("seed_search_query_from_cursor"),
3152                    pick: |settings_content| {
3153                        settings_content
3154                            .editor
3155                            .seed_search_query_from_cursor
3156                            .as_ref()
3157                    },
3158                    write: |settings_content, value| {
3159                        settings_content.editor.seed_search_query_from_cursor = value;
3160                    },
3161                }),
3162                metadata: None,
3163                files: USER,
3164            }),
3165        ]
3166    }
3167
3168    fn file_finder_section() -> [SettingsPageItem; 5] {
3169        [
3170            SettingsPageItem::SectionHeader("File Finder"),
3171            // todo: null by default
3172            SettingsPageItem::SettingItem(SettingItem {
3173                title: "Include Ignored in Search",
3174                description: "Use gitignored files when searching.",
3175                field: Box::new(SettingField {
3176                    json_path: Some("file_finder.include_ignored"),
3177                    pick: |settings_content| {
3178                        settings_content
3179                            .file_finder
3180                            .as_ref()?
3181                            .include_ignored
3182                            .as_ref()
3183                    },
3184                    write: |settings_content, value| {
3185                        settings_content
3186                            .file_finder
3187                            .get_or_insert_default()
3188                            .include_ignored = value;
3189                    },
3190                }),
3191                metadata: None,
3192                files: USER,
3193            }),
3194            SettingsPageItem::SettingItem(SettingItem {
3195                title: "File Icons",
3196                description: "Show file icons in the file finder.",
3197                field: Box::new(SettingField {
3198                    json_path: Some("file_finder.file_icons"),
3199                    pick: |settings_content| {
3200                        settings_content.file_finder.as_ref()?.file_icons.as_ref()
3201                    },
3202                    write: |settings_content, value| {
3203                        settings_content
3204                            .file_finder
3205                            .get_or_insert_default()
3206                            .file_icons = value;
3207                    },
3208                }),
3209                metadata: None,
3210                files: USER,
3211            }),
3212            SettingsPageItem::SettingItem(SettingItem {
3213                title: "Modal Max Width",
3214                description: "Determines how much space the file finder can take up in relation to the available window width.",
3215                field: Box::new(SettingField {
3216                    json_path: Some("file_finder.modal_max_width"),
3217                    pick: |settings_content| {
3218                        settings_content
3219                            .file_finder
3220                            .as_ref()?
3221                            .modal_max_width
3222                            .as_ref()
3223                    },
3224                    write: |settings_content, value| {
3225                        settings_content
3226                            .file_finder
3227                            .get_or_insert_default()
3228                            .modal_max_width = value;
3229                    },
3230                }),
3231                metadata: None,
3232                files: USER,
3233            }),
3234            SettingsPageItem::SettingItem(SettingItem {
3235                title: "Skip Focus For Active In Search",
3236                description: "Whether the file finder should skip focus for the active file in search results.",
3237                field: Box::new(SettingField {
3238                    json_path: Some("file_finder.skip_focus_for_active_in_search"),
3239                    pick: |settings_content| {
3240                        settings_content
3241                            .file_finder
3242                            .as_ref()?
3243                            .skip_focus_for_active_in_search
3244                            .as_ref()
3245                    },
3246                    write: |settings_content, value| {
3247                        settings_content
3248                            .file_finder
3249                            .get_or_insert_default()
3250                            .skip_focus_for_active_in_search = value;
3251                    },
3252                }),
3253                metadata: None,
3254                files: USER,
3255            }),
3256        ]
3257    }
3258
3259    fn file_scan_section() -> [SettingsPageItem; 5] {
3260        [
3261            SettingsPageItem::SectionHeader("File Scan"),
3262            SettingsPageItem::SettingItem(SettingItem {
3263                title: "File Scan Exclusions",
3264                description: "Files or globs of files that will be excluded by Zed entirely. They will be skipped during file scans, file searches, and not be displayed in the project file tree. Takes precedence over \"File Scan Inclusions\"",
3265                field: Box::new(
3266                    SettingField {
3267                        json_path: Some("file_scan_exclusions"),
3268                        pick: |settings_content| {
3269                            settings_content
3270                                .project
3271                                .worktree
3272                                .file_scan_exclusions
3273                                .as_ref()
3274                        },
3275                        write: |settings_content, value| {
3276                            settings_content.project.worktree.file_scan_exclusions = value;
3277                        },
3278                    }
3279                    .unimplemented(),
3280                ),
3281                metadata: None,
3282                files: USER,
3283            }),
3284            SettingsPageItem::SettingItem(SettingItem {
3285                title: "File Scan Inclusions",
3286                description: "Files or globs of files that will be included by Zed, even when ignored by git. This is useful for files that are not tracked by git, but are still important to your project. Note that globs that are overly broad can slow down Zed's file scanning. \"File Scan Exclusions\" takes precedence over these inclusions",
3287                field: Box::new(
3288                    SettingField {
3289                        json_path: Some("file_scan_inclusions"),
3290                        pick: |settings_content| {
3291                            settings_content
3292                                .project
3293                                .worktree
3294                                .file_scan_inclusions
3295                                .as_ref()
3296                        },
3297                        write: |settings_content, value| {
3298                            settings_content.project.worktree.file_scan_inclusions = value;
3299                        },
3300                    }
3301                    .unimplemented(),
3302                ),
3303                metadata: None,
3304                files: USER,
3305            }),
3306            SettingsPageItem::SettingItem(SettingItem {
3307                title: "Restore File State",
3308                description: "Restore previous file state when reopening.",
3309                field: Box::new(SettingField {
3310                    json_path: Some("restore_on_file_reopen"),
3311                    pick: |settings_content| {
3312                        settings_content.workspace.restore_on_file_reopen.as_ref()
3313                    },
3314                    write: |settings_content, value| {
3315                        settings_content.workspace.restore_on_file_reopen = value;
3316                    },
3317                }),
3318                metadata: None,
3319                files: USER,
3320            }),
3321            SettingsPageItem::SettingItem(SettingItem {
3322                title: "Close on File Delete",
3323                description: "Automatically close files that have been deleted.",
3324                field: Box::new(SettingField {
3325                    json_path: Some("close_on_file_delete"),
3326                    pick: |settings_content| {
3327                        settings_content.workspace.close_on_file_delete.as_ref()
3328                    },
3329                    write: |settings_content, value| {
3330                        settings_content.workspace.close_on_file_delete = value;
3331                    },
3332                }),
3333                metadata: None,
3334                files: USER,
3335            }),
3336        ]
3337    }
3338
3339    SettingsPage {
3340        title: "Search & Files",
3341        items: concat_sections![search_section(), file_finder_section(), file_scan_section()],
3342    }
3343}
3344
3345fn window_and_layout_page() -> SettingsPage {
3346    fn status_bar_section() -> [SettingsPageItem; 10] {
3347        [
3348            SettingsPageItem::SectionHeader("Status Bar"),
3349            SettingsPageItem::SettingItem(SettingItem {
3350                title: "Project Panel Button",
3351                description: "Show the project panel button in the status bar.",
3352                field: Box::new(SettingField {
3353                    json_path: Some("project_panel.button"),
3354                    pick: |settings_content| {
3355                        settings_content.project_panel.as_ref()?.button.as_ref()
3356                    },
3357                    write: |settings_content, value| {
3358                        settings_content
3359                            .project_panel
3360                            .get_or_insert_default()
3361                            .button = value;
3362                    },
3363                }),
3364                metadata: None,
3365                files: USER,
3366            }),
3367            SettingsPageItem::SettingItem(SettingItem {
3368                title: "Active Language Button",
3369                description: "Show the active language button in the status bar.",
3370                field: Box::new(SettingField {
3371                    json_path: Some("status_bar.active_language_button"),
3372                    pick: |settings_content| {
3373                        settings_content
3374                            .status_bar
3375                            .as_ref()?
3376                            .active_language_button
3377                            .as_ref()
3378                    },
3379                    write: |settings_content, value| {
3380                        settings_content
3381                            .status_bar
3382                            .get_or_insert_default()
3383                            .active_language_button = value;
3384                    },
3385                }),
3386                metadata: None,
3387                files: USER,
3388            }),
3389            SettingsPageItem::SettingItem(SettingItem {
3390                title: "Active Encoding Button",
3391                description: "Control when to show the active encoding in the status bar.",
3392                field: Box::new(SettingField {
3393                    json_path: Some("status_bar.active_encoding_button"),
3394                    pick: |settings_content| {
3395                        settings_content
3396                            .status_bar
3397                            .as_ref()?
3398                            .active_encoding_button
3399                            .as_ref()
3400                    },
3401                    write: |settings_content, value| {
3402                        settings_content
3403                            .status_bar
3404                            .get_or_insert_default()
3405                            .active_encoding_button = value;
3406                    },
3407                }),
3408                metadata: None,
3409                files: USER,
3410            }),
3411            SettingsPageItem::SettingItem(SettingItem {
3412                title: "Cursor Position Button",
3413                description: "Show the cursor position button in the status bar.",
3414                field: Box::new(SettingField {
3415                    json_path: Some("status_bar.cursor_position_button"),
3416                    pick: |settings_content| {
3417                        settings_content
3418                            .status_bar
3419                            .as_ref()?
3420                            .cursor_position_button
3421                            .as_ref()
3422                    },
3423                    write: |settings_content, value| {
3424                        settings_content
3425                            .status_bar
3426                            .get_or_insert_default()
3427                            .cursor_position_button = value;
3428                    },
3429                }),
3430                metadata: None,
3431                files: USER,
3432            }),
3433            SettingsPageItem::SettingItem(SettingItem {
3434                title: "Terminal Button",
3435                description: "Show the terminal button in the status bar.",
3436                field: Box::new(SettingField {
3437                    json_path: Some("terminal.button"),
3438                    pick: |settings_content| settings_content.terminal.as_ref()?.button.as_ref(),
3439                    write: |settings_content, value| {
3440                        settings_content.terminal.get_or_insert_default().button = value;
3441                    },
3442                }),
3443                metadata: None,
3444                files: USER,
3445            }),
3446            SettingsPageItem::SettingItem(SettingItem {
3447                title: "Diagnostics Button",
3448                description: "Show the project diagnostics button in the status bar.",
3449                field: Box::new(SettingField {
3450                    json_path: Some("diagnostics.button"),
3451                    pick: |settings_content| settings_content.diagnostics.as_ref()?.button.as_ref(),
3452                    write: |settings_content, value| {
3453                        settings_content.diagnostics.get_or_insert_default().button = value;
3454                    },
3455                }),
3456                metadata: None,
3457                files: USER,
3458            }),
3459            SettingsPageItem::SettingItem(SettingItem {
3460                title: "Project Search Button",
3461                description: "Show the project search button in the status bar.",
3462                field: Box::new(SettingField {
3463                    json_path: Some("search.button"),
3464                    pick: |settings_content| {
3465                        settings_content.editor.search.as_ref()?.button.as_ref()
3466                    },
3467                    write: |settings_content, value| {
3468                        settings_content
3469                            .editor
3470                            .search
3471                            .get_or_insert_default()
3472                            .button = value;
3473                    },
3474                }),
3475                metadata: None,
3476                files: USER,
3477            }),
3478            SettingsPageItem::SettingItem(SettingItem {
3479                title: "Debugger Button",
3480                description: "Show the debugger button in the status bar.",
3481                field: Box::new(SettingField {
3482                    json_path: Some("debugger.button"),
3483                    pick: |settings_content| settings_content.debugger.as_ref()?.button.as_ref(),
3484                    write: |settings_content, value| {
3485                        settings_content.debugger.get_or_insert_default().button = value;
3486                    },
3487                }),
3488                metadata: None,
3489                files: USER,
3490            }),
3491            SettingsPageItem::SettingItem(SettingItem {
3492                title: "Active File Name",
3493                description: "Show the name of the active file in the status bar.",
3494                field: Box::new(SettingField {
3495                    json_path: Some("status_bar.show_active_file"),
3496                    pick: |settings_content| {
3497                        settings_content
3498                            .status_bar
3499                            .as_ref()?
3500                            .show_active_file
3501                            .as_ref()
3502                    },
3503                    write: |settings_content, value| {
3504                        settings_content
3505                            .status_bar
3506                            .get_or_insert_default()
3507                            .show_active_file = value;
3508                    },
3509                }),
3510                metadata: None,
3511                files: USER,
3512            }),
3513        ]
3514    }
3515
3516    fn title_bar_section() -> [SettingsPageItem; 10] {
3517        [
3518            SettingsPageItem::SectionHeader("Title Bar"),
3519            SettingsPageItem::SettingItem(SettingItem {
3520                title: "Show Branch Icon",
3521                description: "Show the branch icon beside branch switcher in the titlebar.",
3522                field: Box::new(SettingField {
3523                    json_path: Some("title_bar.show_branch_icon"),
3524                    pick: |settings_content| {
3525                        settings_content
3526                            .title_bar
3527                            .as_ref()?
3528                            .show_branch_icon
3529                            .as_ref()
3530                    },
3531                    write: |settings_content, value| {
3532                        settings_content
3533                            .title_bar
3534                            .get_or_insert_default()
3535                            .show_branch_icon = value;
3536                    },
3537                }),
3538                metadata: None,
3539                files: USER,
3540            }),
3541            SettingsPageItem::SettingItem(SettingItem {
3542                title: "Show Branch Name",
3543                description: "Show the branch name button in the titlebar.",
3544                field: Box::new(SettingField {
3545                    json_path: Some("title_bar.show_branch_name"),
3546                    pick: |settings_content| {
3547                        settings_content
3548                            .title_bar
3549                            .as_ref()?
3550                            .show_branch_name
3551                            .as_ref()
3552                    },
3553                    write: |settings_content, value| {
3554                        settings_content
3555                            .title_bar
3556                            .get_or_insert_default()
3557                            .show_branch_name = value;
3558                    },
3559                }),
3560                metadata: None,
3561                files: USER,
3562            }),
3563            SettingsPageItem::SettingItem(SettingItem {
3564                title: "Show Project Items",
3565                description: "Show the project host and name in the titlebar.",
3566                field: Box::new(SettingField {
3567                    json_path: Some("title_bar.show_project_items"),
3568                    pick: |settings_content| {
3569                        settings_content
3570                            .title_bar
3571                            .as_ref()?
3572                            .show_project_items
3573                            .as_ref()
3574                    },
3575                    write: |settings_content, value| {
3576                        settings_content
3577                            .title_bar
3578                            .get_or_insert_default()
3579                            .show_project_items = value;
3580                    },
3581                }),
3582                metadata: None,
3583                files: USER,
3584            }),
3585            SettingsPageItem::SettingItem(SettingItem {
3586                title: "Show Onboarding Banner",
3587                description: "Show banners announcing new features in the titlebar.",
3588                field: Box::new(SettingField {
3589                    json_path: Some("title_bar.show_onboarding_banner"),
3590                    pick: |settings_content| {
3591                        settings_content
3592                            .title_bar
3593                            .as_ref()?
3594                            .show_onboarding_banner
3595                            .as_ref()
3596                    },
3597                    write: |settings_content, value| {
3598                        settings_content
3599                            .title_bar
3600                            .get_or_insert_default()
3601                            .show_onboarding_banner = value;
3602                    },
3603                }),
3604                metadata: None,
3605                files: USER,
3606            }),
3607            SettingsPageItem::SettingItem(SettingItem {
3608                title: "Show Sign In",
3609                description: "Show the sign in button in the titlebar.",
3610                field: Box::new(SettingField {
3611                    json_path: Some("title_bar.show_sign_in"),
3612                    pick: |settings_content| {
3613                        settings_content.title_bar.as_ref()?.show_sign_in.as_ref()
3614                    },
3615                    write: |settings_content, value| {
3616                        settings_content
3617                            .title_bar
3618                            .get_or_insert_default()
3619                            .show_sign_in = value;
3620                    },
3621                }),
3622                metadata: None,
3623                files: USER,
3624            }),
3625            SettingsPageItem::SettingItem(SettingItem {
3626                title: "Show User Menu",
3627                description: "Show the user menu button in the titlebar.",
3628                field: Box::new(SettingField {
3629                    json_path: Some("title_bar.show_user_menu"),
3630                    pick: |settings_content| {
3631                        settings_content.title_bar.as_ref()?.show_user_menu.as_ref()
3632                    },
3633                    write: |settings_content, value| {
3634                        settings_content
3635                            .title_bar
3636                            .get_or_insert_default()
3637                            .show_user_menu = value;
3638                    },
3639                }),
3640                metadata: None,
3641                files: USER,
3642            }),
3643            SettingsPageItem::SettingItem(SettingItem {
3644                title: "Show User Picture",
3645                description: "Show user picture in the titlebar.",
3646                field: Box::new(SettingField {
3647                    json_path: Some("title_bar.show_user_picture"),
3648                    pick: |settings_content| {
3649                        settings_content
3650                            .title_bar
3651                            .as_ref()?
3652                            .show_user_picture
3653                            .as_ref()
3654                    },
3655                    write: |settings_content, value| {
3656                        settings_content
3657                            .title_bar
3658                            .get_or_insert_default()
3659                            .show_user_picture = value;
3660                    },
3661                }),
3662                metadata: None,
3663                files: USER,
3664            }),
3665            SettingsPageItem::SettingItem(SettingItem {
3666                title: "Show Menus",
3667                description: "Show the menus in the titlebar.",
3668                field: Box::new(SettingField {
3669                    json_path: Some("title_bar.show_menus"),
3670                    pick: |settings_content| {
3671                        settings_content.title_bar.as_ref()?.show_menus.as_ref()
3672                    },
3673                    write: |settings_content, value| {
3674                        settings_content
3675                            .title_bar
3676                            .get_or_insert_default()
3677                            .show_menus = value;
3678                    },
3679                }),
3680                metadata: None,
3681                files: USER,
3682            }),
3683            SettingsPageItem::DynamicItem(DynamicItem {
3684                discriminant: SettingItem {
3685                    files: USER,
3686                    title: "Button Layout",
3687                    description:
3688                        "(Linux only) choose how window control buttons are laid out in the titlebar.",
3689                    field: Box::new(SettingField {
3690                        json_path: Some("title_bar.button_layout$"),
3691                        pick: |settings_content| {
3692                            Some(
3693                                &dynamic_variants::<settings::WindowButtonLayoutContent>()[settings_content
3694                                    .title_bar
3695                                    .as_ref()?
3696                                    .button_layout
3697                                    .as_ref()?
3698                                    .discriminant()
3699                                    as usize],
3700                            )
3701                        },
3702                        write: |settings_content, value| {
3703                            let Some(value) = value else {
3704                                settings_content
3705                                    .title_bar
3706                                    .get_or_insert_default()
3707                                    .button_layout = None;
3708                                return;
3709                            };
3710
3711                            let current_custom_layout = settings_content
3712                                .title_bar
3713                                .as_ref()
3714                                .and_then(|title_bar| title_bar.button_layout.as_ref())
3715                                .and_then(|button_layout| match button_layout {
3716                                    settings::WindowButtonLayoutContent::Custom(layout) => {
3717                                        Some(layout.clone())
3718                                    }
3719                                    _ => None,
3720                                });
3721
3722                            let button_layout = match value {
3723                                settings::WindowButtonLayoutContentDiscriminants::PlatformDefault => {
3724                                    settings::WindowButtonLayoutContent::PlatformDefault
3725                                }
3726                                settings::WindowButtonLayoutContentDiscriminants::Standard => {
3727                                    settings::WindowButtonLayoutContent::Standard
3728                                }
3729                                settings::WindowButtonLayoutContentDiscriminants::Custom => {
3730                                    settings::WindowButtonLayoutContent::Custom(
3731                                        current_custom_layout.unwrap_or_else(|| {
3732                                            "close:minimize,maximize".to_string()
3733                                        }),
3734                                    )
3735                                }
3736                            };
3737
3738                            settings_content
3739                                .title_bar
3740                                .get_or_insert_default()
3741                                .button_layout = Some(button_layout);
3742                        },
3743                    }),
3744                    metadata: None,
3745                },
3746                pick_discriminant: |settings_content| {
3747                    Some(
3748                        settings_content
3749                            .title_bar
3750                            .as_ref()?
3751                            .button_layout
3752                            .as_ref()?
3753                            .discriminant() as usize,
3754                    )
3755                },
3756                fields: dynamic_variants::<settings::WindowButtonLayoutContent>()
3757                    .into_iter()
3758                    .map(|variant| match variant {
3759                        settings::WindowButtonLayoutContentDiscriminants::PlatformDefault => {
3760                            vec![]
3761                        }
3762                        settings::WindowButtonLayoutContentDiscriminants::Standard => vec![],
3763                        settings::WindowButtonLayoutContentDiscriminants::Custom => vec![
3764                            SettingItem {
3765                                files: USER,
3766                                title: "Custom Button Layout",
3767                                description:
3768                                    "GNOME-style layout string such as \"close:minimize,maximize\".",
3769                                field: Box::new(SettingField {
3770                                    json_path: Some("title_bar.button_layout"),
3771                                    pick: |settings_content| match settings_content
3772                                        .title_bar
3773                                        .as_ref()?
3774                                        .button_layout
3775                                        .as_ref()?
3776                                    {
3777                                        settings::WindowButtonLayoutContent::Custom(layout) => {
3778                                            Some(layout)
3779                                        }
3780                                        _ => DEFAULT_EMPTY_STRING,
3781                                    },
3782                                    write: |settings_content, value| {
3783                                        settings_content
3784                                            .title_bar
3785                                            .get_or_insert_default()
3786                                            .button_layout = value
3787                                            .map(settings::WindowButtonLayoutContent::Custom);
3788                                    },
3789                                }),
3790                                metadata: Some(Box::new(SettingsFieldMetadata {
3791                                    placeholder: Some("close:minimize,maximize"),
3792                                    ..Default::default()
3793                                })),
3794                            },
3795                        ],
3796                    })
3797                    .collect(),
3798            }),
3799        ]
3800    }
3801
3802    fn tab_bar_section() -> [SettingsPageItem; 9] {
3803        [
3804            SettingsPageItem::SectionHeader("Tab Bar"),
3805            SettingsPageItem::SettingItem(SettingItem {
3806                title: "Show Tab Bar",
3807                description: "Show the tab bar in the editor.",
3808                field: Box::new(SettingField {
3809                    json_path: Some("tab_bar.show"),
3810                    pick: |settings_content| settings_content.tab_bar.as_ref()?.show.as_ref(),
3811                    write: |settings_content, value| {
3812                        settings_content.tab_bar.get_or_insert_default().show = value;
3813                    },
3814                }),
3815                metadata: None,
3816                files: USER,
3817            }),
3818            SettingsPageItem::SettingItem(SettingItem {
3819                title: "Show Git Status In Tabs",
3820                description: "Show the Git file status on a tab item.",
3821                field: Box::new(SettingField {
3822                    json_path: Some("tabs.git_status"),
3823                    pick: |settings_content| settings_content.tabs.as_ref()?.git_status.as_ref(),
3824                    write: |settings_content, value| {
3825                        settings_content.tabs.get_or_insert_default().git_status = value;
3826                    },
3827                }),
3828                metadata: None,
3829                files: USER,
3830            }),
3831            SettingsPageItem::SettingItem(SettingItem {
3832                title: "Show File Icons In Tabs",
3833                description: "Show the file icon for a tab.",
3834                field: Box::new(SettingField {
3835                    json_path: Some("tabs.file_icons"),
3836                    pick: |settings_content| settings_content.tabs.as_ref()?.file_icons.as_ref(),
3837                    write: |settings_content, value| {
3838                        settings_content.tabs.get_or_insert_default().file_icons = value;
3839                    },
3840                }),
3841                metadata: None,
3842                files: USER,
3843            }),
3844            SettingsPageItem::SettingItem(SettingItem {
3845                title: "Tab Close Position",
3846                description: "Position of the close button in a tab.",
3847                field: Box::new(SettingField {
3848                    json_path: Some("tabs.close_position"),
3849                    pick: |settings_content| {
3850                        settings_content.tabs.as_ref()?.close_position.as_ref()
3851                    },
3852                    write: |settings_content, value| {
3853                        settings_content.tabs.get_or_insert_default().close_position = value;
3854                    },
3855                }),
3856                metadata: None,
3857                files: USER,
3858            }),
3859            SettingsPageItem::SettingItem(SettingItem {
3860                files: USER,
3861                title: "Maximum Tabs",
3862                description: "Maximum open tabs in a pane. Will not close an unsaved tab.",
3863                // todo(settings_ui): The default for this value is null and it's use in code
3864                // is complex, so I'm going to come back to this later
3865                field: Box::new(
3866                    SettingField {
3867                        json_path: Some("max_tabs"),
3868                        pick: |settings_content| settings_content.workspace.max_tabs.as_ref(),
3869                        write: |settings_content, value| {
3870                            settings_content.workspace.max_tabs = value;
3871                        },
3872                    }
3873                    .unimplemented(),
3874                ),
3875                metadata: None,
3876            }),
3877            SettingsPageItem::SettingItem(SettingItem {
3878                title: "Show Navigation History Buttons",
3879                description: "Show the navigation history buttons in the tab bar.",
3880                field: Box::new(SettingField {
3881                    json_path: Some("tab_bar.show_nav_history_buttons"),
3882                    pick: |settings_content| {
3883                        settings_content
3884                            .tab_bar
3885                            .as_ref()?
3886                            .show_nav_history_buttons
3887                            .as_ref()
3888                    },
3889                    write: |settings_content, value| {
3890                        settings_content
3891                            .tab_bar
3892                            .get_or_insert_default()
3893                            .show_nav_history_buttons = value;
3894                    },
3895                }),
3896                metadata: None,
3897                files: USER,
3898            }),
3899            SettingsPageItem::SettingItem(SettingItem {
3900                title: "Show Tab Bar Buttons",
3901                description: "Show the tab bar buttons (New, Split Pane, Zoom).",
3902                field: Box::new(SettingField {
3903                    json_path: Some("tab_bar.show_tab_bar_buttons"),
3904                    pick: |settings_content| {
3905                        settings_content
3906                            .tab_bar
3907                            .as_ref()?
3908                            .show_tab_bar_buttons
3909                            .as_ref()
3910                    },
3911                    write: |settings_content, value| {
3912                        settings_content
3913                            .tab_bar
3914                            .get_or_insert_default()
3915                            .show_tab_bar_buttons = value;
3916                    },
3917                }),
3918                metadata: None,
3919                files: USER,
3920            }),
3921            SettingsPageItem::SettingItem(SettingItem {
3922                title: "Pinned Tabs Layout",
3923                description: "Show pinned tabs in a separate row above unpinned tabs.",
3924                field: Box::new(SettingField {
3925                    json_path: Some("tab_bar.show_pinned_tabs_in_separate_row"),
3926                    pick: |settings_content| {
3927                        settings_content
3928                            .tab_bar
3929                            .as_ref()?
3930                            .show_pinned_tabs_in_separate_row
3931                            .as_ref()
3932                    },
3933                    write: |settings_content, value| {
3934                        settings_content
3935                            .tab_bar
3936                            .get_or_insert_default()
3937                            .show_pinned_tabs_in_separate_row = value;
3938                    },
3939                }),
3940                metadata: None,
3941                files: USER,
3942            }),
3943        ]
3944    }
3945
3946    fn tab_settings_section() -> [SettingsPageItem; 4] {
3947        [
3948            SettingsPageItem::SectionHeader("Tab Settings"),
3949            SettingsPageItem::SettingItem(SettingItem {
3950                title: "Activate On Close",
3951                description: "What to do after closing the current tab.",
3952                field: Box::new(SettingField {
3953                    json_path: Some("tabs.activate_on_close"),
3954                    pick: |settings_content| {
3955                        settings_content.tabs.as_ref()?.activate_on_close.as_ref()
3956                    },
3957                    write: |settings_content, value| {
3958                        settings_content
3959                            .tabs
3960                            .get_or_insert_default()
3961                            .activate_on_close = value;
3962                    },
3963                }),
3964                metadata: None,
3965                files: USER,
3966            }),
3967            SettingsPageItem::SettingItem(SettingItem {
3968                title: "Tab Show Diagnostics",
3969                description: "Which files containing diagnostic errors/warnings to mark in the tabs.",
3970                field: Box::new(SettingField {
3971                    json_path: Some("tabs.show_diagnostics"),
3972                    pick: |settings_content| {
3973                        settings_content.tabs.as_ref()?.show_diagnostics.as_ref()
3974                    },
3975                    write: |settings_content, value| {
3976                        settings_content
3977                            .tabs
3978                            .get_or_insert_default()
3979                            .show_diagnostics = value;
3980                    },
3981                }),
3982                metadata: None,
3983                files: USER,
3984            }),
3985            SettingsPageItem::SettingItem(SettingItem {
3986                title: "Show Close Button",
3987                description: "Controls the appearance behavior of the tab's close button.",
3988                field: Box::new(SettingField {
3989                    json_path: Some("tabs.show_close_button"),
3990                    pick: |settings_content| {
3991                        settings_content.tabs.as_ref()?.show_close_button.as_ref()
3992                    },
3993                    write: |settings_content, value| {
3994                        settings_content
3995                            .tabs
3996                            .get_or_insert_default()
3997                            .show_close_button = value;
3998                    },
3999                }),
4000                metadata: None,
4001                files: USER,
4002            }),
4003        ]
4004    }
4005
4006    fn preview_tabs_section() -> [SettingsPageItem; 8] {
4007        [
4008            SettingsPageItem::SectionHeader("Preview Tabs"),
4009            SettingsPageItem::SettingItem(SettingItem {
4010                title: "Preview Tabs Enabled",
4011                description: "Show opened editors as preview tabs.",
4012                field: Box::new(SettingField {
4013                    json_path: Some("preview_tabs.enabled"),
4014                    pick: |settings_content| {
4015                        settings_content.preview_tabs.as_ref()?.enabled.as_ref()
4016                    },
4017                    write: |settings_content, value| {
4018                        settings_content
4019                            .preview_tabs
4020                            .get_or_insert_default()
4021                            .enabled = value;
4022                    },
4023                }),
4024                metadata: None,
4025                files: USER,
4026            }),
4027            SettingsPageItem::SettingItem(SettingItem {
4028                title: "Enable Preview From Project Panel",
4029                description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
4030                field: Box::new(SettingField {
4031                    json_path: Some("preview_tabs.enable_preview_from_project_panel"),
4032                    pick: |settings_content| {
4033                        settings_content
4034                            .preview_tabs
4035                            .as_ref()?
4036                            .enable_preview_from_project_panel
4037                            .as_ref()
4038                    },
4039                    write: |settings_content, value| {
4040                        settings_content
4041                            .preview_tabs
4042                            .get_or_insert_default()
4043                            .enable_preview_from_project_panel = value;
4044                    },
4045                }),
4046                metadata: None,
4047                files: USER,
4048            }),
4049            SettingsPageItem::SettingItem(SettingItem {
4050                title: "Enable Preview From File Finder",
4051                description: "Whether to open tabs in preview mode when selected from the file finder.",
4052                field: Box::new(SettingField {
4053                    json_path: Some("preview_tabs.enable_preview_from_file_finder"),
4054                    pick: |settings_content| {
4055                        settings_content
4056                            .preview_tabs
4057                            .as_ref()?
4058                            .enable_preview_from_file_finder
4059                            .as_ref()
4060                    },
4061                    write: |settings_content, value| {
4062                        settings_content
4063                            .preview_tabs
4064                            .get_or_insert_default()
4065                            .enable_preview_from_file_finder = value;
4066                    },
4067                }),
4068                metadata: None,
4069                files: USER,
4070            }),
4071            SettingsPageItem::SettingItem(SettingItem {
4072                title: "Enable Preview From Multibuffer",
4073                description: "Whether to open tabs in preview mode when opened from a multibuffer.",
4074                field: Box::new(SettingField {
4075                    json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
4076                    pick: |settings_content| {
4077                        settings_content
4078                            .preview_tabs
4079                            .as_ref()?
4080                            .enable_preview_from_multibuffer
4081                            .as_ref()
4082                    },
4083                    write: |settings_content, value| {
4084                        settings_content
4085                            .preview_tabs
4086                            .get_or_insert_default()
4087                            .enable_preview_from_multibuffer = value;
4088                    },
4089                }),
4090                metadata: None,
4091                files: USER,
4092            }),
4093            SettingsPageItem::SettingItem(SettingItem {
4094                title: "Enable Preview Multibuffer From Code Navigation",
4095                description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
4096                field: Box::new(SettingField {
4097                    json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
4098                    pick: |settings_content| {
4099                        settings_content
4100                            .preview_tabs
4101                            .as_ref()?
4102                            .enable_preview_multibuffer_from_code_navigation
4103                            .as_ref()
4104                    },
4105                    write: |settings_content, value| {
4106                        settings_content
4107                            .preview_tabs
4108                            .get_or_insert_default()
4109                            .enable_preview_multibuffer_from_code_navigation = value;
4110                    },
4111                }),
4112                metadata: None,
4113                files: USER,
4114            }),
4115            SettingsPageItem::SettingItem(SettingItem {
4116                title: "Enable Preview File From Code Navigation",
4117                description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
4118                field: Box::new(SettingField {
4119                    json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
4120                    pick: |settings_content| {
4121                        settings_content
4122                            .preview_tabs
4123                            .as_ref()?
4124                            .enable_preview_file_from_code_navigation
4125                            .as_ref()
4126                    },
4127                    write: |settings_content, value| {
4128                        settings_content
4129                            .preview_tabs
4130                            .get_or_insert_default()
4131                            .enable_preview_file_from_code_navigation = value;
4132                    },
4133                }),
4134                metadata: None,
4135                files: USER,
4136            }),
4137            SettingsPageItem::SettingItem(SettingItem {
4138                title: "Enable Keep Preview On Code Navigation",
4139                description: "Whether to keep tabs in preview mode when code navigation is used to navigate away from them. If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.",
4140                field: Box::new(SettingField {
4141                    json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
4142                    pick: |settings_content| {
4143                        settings_content
4144                            .preview_tabs
4145                            .as_ref()?
4146                            .enable_keep_preview_on_code_navigation
4147                            .as_ref()
4148                    },
4149                    write: |settings_content, value| {
4150                        settings_content
4151                            .preview_tabs
4152                            .get_or_insert_default()
4153                            .enable_keep_preview_on_code_navigation = value;
4154                    },
4155                }),
4156                metadata: None,
4157                files: USER,
4158            }),
4159        ]
4160    }
4161
4162    fn layout_section() -> [SettingsPageItem; 6] {
4163        [
4164            SettingsPageItem::SectionHeader("Layout"),
4165            SettingsPageItem::SettingItem(SettingItem {
4166                title: "Bottom Dock Layout",
4167                description: "Layout mode for the bottom dock.",
4168                field: Box::new(SettingField {
4169                    json_path: Some("bottom_dock_layout"),
4170                    pick: |settings_content| settings_content.workspace.bottom_dock_layout.as_ref(),
4171                    write: |settings_content, value| {
4172                        settings_content.workspace.bottom_dock_layout = value;
4173                    },
4174                }),
4175                metadata: None,
4176                files: USER,
4177            }),
4178            SettingsPageItem::SettingItem(SettingItem {
4179                files: USER,
4180                title: "Centered Layout Left Padding",
4181                description: "Left padding for centered layout.",
4182                field: Box::new(SettingField {
4183                    json_path: Some("centered_layout.left_padding"),
4184                    pick: |settings_content| {
4185                        settings_content
4186                            .workspace
4187                            .centered_layout
4188                            .as_ref()?
4189                            .left_padding
4190                            .as_ref()
4191                    },
4192                    write: |settings_content, value| {
4193                        settings_content
4194                            .workspace
4195                            .centered_layout
4196                            .get_or_insert_default()
4197                            .left_padding = value;
4198                    },
4199                }),
4200                metadata: None,
4201            }),
4202            SettingsPageItem::SettingItem(SettingItem {
4203                files: USER,
4204                title: "Centered Layout Right Padding",
4205                description: "Right padding for centered layout.",
4206                field: Box::new(SettingField {
4207                    json_path: Some("centered_layout.right_padding"),
4208                    pick: |settings_content| {
4209                        settings_content
4210                            .workspace
4211                            .centered_layout
4212                            .as_ref()?
4213                            .right_padding
4214                            .as_ref()
4215                    },
4216                    write: |settings_content, value| {
4217                        settings_content
4218                            .workspace
4219                            .centered_layout
4220                            .get_or_insert_default()
4221                            .right_padding = value;
4222                    },
4223                }),
4224                metadata: None,
4225            }),
4226            SettingsPageItem::SettingItem(SettingItem {
4227                title: "Focus Follows Mouse",
4228                description: "Whether to change focus to a pane when the mouse hovers over it.",
4229                field: Box::new(SettingField {
4230                    json_path: Some("focus_follows_mouse.enabled"),
4231                    pick: |settings_content| {
4232                        settings_content
4233                            .workspace
4234                            .focus_follows_mouse
4235                            .as_ref()
4236                            .and_then(|s| s.enabled.as_ref())
4237                    },
4238                    write: |settings_content, value| {
4239                        settings_content
4240                            .workspace
4241                            .focus_follows_mouse
4242                            .get_or_insert_default()
4243                            .enabled = value;
4244                    },
4245                }),
4246                metadata: None,
4247                files: USER,
4248            }),
4249            SettingsPageItem::SettingItem(SettingItem {
4250                title: "Focus Follows Mouse Debounce ms",
4251                description: "Amount of time to wait before changing focus.",
4252                field: Box::new(SettingField {
4253                    json_path: Some("focus_follows_mouse.debounce_ms"),
4254                    pick: |settings_content| {
4255                        settings_content
4256                            .workspace
4257                            .focus_follows_mouse
4258                            .as_ref()
4259                            .and_then(|s| s.debounce_ms.as_ref())
4260                    },
4261                    write: |settings_content, value| {
4262                        settings_content
4263                            .workspace
4264                            .focus_follows_mouse
4265                            .get_or_insert_default()
4266                            .debounce_ms = value;
4267                    },
4268                }),
4269                metadata: None,
4270                files: USER,
4271            }),
4272        ]
4273    }
4274
4275    fn window_section() -> [SettingsPageItem; 3] {
4276        [
4277            SettingsPageItem::SectionHeader("Window"),
4278            // todo(settings_ui): Should we filter by platform.as_ref()?
4279            SettingsPageItem::SettingItem(SettingItem {
4280                title: "Use System Window Tabs",
4281                description: "(macOS only) whether to allow Windows to tab together.",
4282                field: Box::new(SettingField {
4283                    json_path: Some("use_system_window_tabs"),
4284                    pick: |settings_content| {
4285                        settings_content.workspace.use_system_window_tabs.as_ref()
4286                    },
4287                    write: |settings_content, value| {
4288                        settings_content.workspace.use_system_window_tabs = value;
4289                    },
4290                }),
4291                metadata: None,
4292                files: USER,
4293            }),
4294            SettingsPageItem::SettingItem(SettingItem {
4295                title: "Window Decorations",
4296                description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4297                field: Box::new(SettingField {
4298                    json_path: Some("window_decorations"),
4299                    pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4300                    write: |settings_content, value| {
4301                        settings_content.workspace.window_decorations = value;
4302                    },
4303                }),
4304                metadata: None,
4305                files: USER,
4306            }),
4307        ]
4308    }
4309
4310    fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4311        [
4312            SettingsPageItem::SectionHeader("Pane Modifiers"),
4313            SettingsPageItem::SettingItem(SettingItem {
4314                title: "Inactive Opacity",
4315                description: "Opacity of inactive panels (0.0 - 1.0).",
4316                field: Box::new(SettingField {
4317                    json_path: Some("active_pane_modifiers.inactive_opacity"),
4318                    pick: |settings_content| {
4319                        settings_content
4320                            .workspace
4321                            .active_pane_modifiers
4322                            .as_ref()?
4323                            .inactive_opacity
4324                            .as_ref()
4325                    },
4326                    write: |settings_content, value| {
4327                        settings_content
4328                            .workspace
4329                            .active_pane_modifiers
4330                            .get_or_insert_default()
4331                            .inactive_opacity = value;
4332                    },
4333                }),
4334                metadata: None,
4335                files: USER,
4336            }),
4337            SettingsPageItem::SettingItem(SettingItem {
4338                title: "Border Size",
4339                description: "Size of the border surrounding the active pane.",
4340                field: Box::new(SettingField {
4341                    json_path: Some("active_pane_modifiers.border_size"),
4342                    pick: |settings_content| {
4343                        settings_content
4344                            .workspace
4345                            .active_pane_modifiers
4346                            .as_ref()?
4347                            .border_size
4348                            .as_ref()
4349                    },
4350                    write: |settings_content, value| {
4351                        settings_content
4352                            .workspace
4353                            .active_pane_modifiers
4354                            .get_or_insert_default()
4355                            .border_size = value;
4356                    },
4357                }),
4358                metadata: None,
4359                files: USER,
4360            }),
4361            SettingsPageItem::SettingItem(SettingItem {
4362                title: "Zoomed Padding",
4363                description: "Show padding for zoomed panes.",
4364                field: Box::new(SettingField {
4365                    json_path: Some("zoomed_padding"),
4366                    pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4367                    write: |settings_content, value| {
4368                        settings_content.workspace.zoomed_padding = value;
4369                    },
4370                }),
4371                metadata: None,
4372                files: USER,
4373            }),
4374        ]
4375    }
4376
4377    fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4378        [
4379            SettingsPageItem::SectionHeader("Pane Split Direction"),
4380            SettingsPageItem::SettingItem(SettingItem {
4381                title: "Vertical Split Direction",
4382                description: "Direction to split vertically.",
4383                field: Box::new(SettingField {
4384                    json_path: Some("pane_split_direction_vertical"),
4385                    pick: |settings_content| {
4386                        settings_content
4387                            .workspace
4388                            .pane_split_direction_vertical
4389                            .as_ref()
4390                    },
4391                    write: |settings_content, value| {
4392                        settings_content.workspace.pane_split_direction_vertical = value;
4393                    },
4394                }),
4395                metadata: None,
4396                files: USER,
4397            }),
4398            SettingsPageItem::SettingItem(SettingItem {
4399                title: "Horizontal Split Direction",
4400                description: "Direction to split horizontally.",
4401                field: Box::new(SettingField {
4402                    json_path: Some("pane_split_direction_horizontal"),
4403                    pick: |settings_content| {
4404                        settings_content
4405                            .workspace
4406                            .pane_split_direction_horizontal
4407                            .as_ref()
4408                    },
4409                    write: |settings_content, value| {
4410                        settings_content.workspace.pane_split_direction_horizontal = value;
4411                    },
4412                }),
4413                metadata: None,
4414                files: USER,
4415            }),
4416        ]
4417    }
4418
4419    SettingsPage {
4420        title: "Window & Layout",
4421        items: concat_sections![
4422            status_bar_section(),
4423            title_bar_section(),
4424            tab_bar_section(),
4425            tab_settings_section(),
4426            preview_tabs_section(),
4427            layout_section(),
4428            window_section(),
4429            pane_modifiers_section(),
4430            pane_split_direction_section(),
4431        ],
4432    }
4433}
4434
4435fn panels_page() -> SettingsPage {
4436    fn project_panel_section() -> [SettingsPageItem; 24] {
4437        [
4438            SettingsPageItem::SectionHeader("Project Panel"),
4439            SettingsPageItem::SettingItem(SettingItem {
4440                title: "Project Panel Dock",
4441                description: "Where to dock the project panel.",
4442                field: Box::new(SettingField {
4443                    json_path: Some("project_panel.dock"),
4444                    pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4445                    write: |settings_content, value| {
4446                        settings_content.project_panel.get_or_insert_default().dock = value;
4447                    },
4448                }),
4449                metadata: None,
4450                files: USER,
4451            }),
4452            SettingsPageItem::SettingItem(SettingItem {
4453                title: "Project Panel Default Width",
4454                description: "Default width of the project panel in pixels.",
4455                field: Box::new(SettingField {
4456                    json_path: Some("project_panel.default_width"),
4457                    pick: |settings_content| {
4458                        settings_content
4459                            .project_panel
4460                            .as_ref()?
4461                            .default_width
4462                            .as_ref()
4463                    },
4464                    write: |settings_content, value| {
4465                        settings_content
4466                            .project_panel
4467                            .get_or_insert_default()
4468                            .default_width = value;
4469                    },
4470                }),
4471                metadata: None,
4472                files: USER,
4473            }),
4474            SettingsPageItem::SettingItem(SettingItem {
4475                title: "Hide .gitignore",
4476                description: "Whether to hide the gitignore entries in the project panel.",
4477                field: Box::new(SettingField {
4478                    json_path: Some("project_panel.hide_gitignore"),
4479                    pick: |settings_content| {
4480                        settings_content
4481                            .project_panel
4482                            .as_ref()?
4483                            .hide_gitignore
4484                            .as_ref()
4485                    },
4486                    write: |settings_content, value| {
4487                        settings_content
4488                            .project_panel
4489                            .get_or_insert_default()
4490                            .hide_gitignore = value;
4491                    },
4492                }),
4493                metadata: None,
4494                files: USER,
4495            }),
4496            SettingsPageItem::SettingItem(SettingItem {
4497                title: "Entry Spacing",
4498                description: "Spacing between worktree entries in the project panel.",
4499                field: Box::new(SettingField {
4500                    json_path: Some("project_panel.entry_spacing"),
4501                    pick: |settings_content| {
4502                        settings_content
4503                            .project_panel
4504                            .as_ref()?
4505                            .entry_spacing
4506                            .as_ref()
4507                    },
4508                    write: |settings_content, value| {
4509                        settings_content
4510                            .project_panel
4511                            .get_or_insert_default()
4512                            .entry_spacing = value;
4513                    },
4514                }),
4515                metadata: None,
4516                files: USER,
4517            }),
4518            SettingsPageItem::SettingItem(SettingItem {
4519                title: "File Icons",
4520                description: "Show file icons in the project panel.",
4521                field: Box::new(SettingField {
4522                    json_path: Some("project_panel.file_icons"),
4523                    pick: |settings_content| {
4524                        settings_content.project_panel.as_ref()?.file_icons.as_ref()
4525                    },
4526                    write: |settings_content, value| {
4527                        settings_content
4528                            .project_panel
4529                            .get_or_insert_default()
4530                            .file_icons = value;
4531                    },
4532                }),
4533                metadata: None,
4534                files: USER,
4535            }),
4536            SettingsPageItem::SettingItem(SettingItem {
4537                title: "Folder Icons",
4538                description: "Whether to show folder icons or chevrons for directories in the project panel.",
4539                field: Box::new(SettingField {
4540                    json_path: Some("project_panel.folder_icons"),
4541                    pick: |settings_content| {
4542                        settings_content
4543                            .project_panel
4544                            .as_ref()?
4545                            .folder_icons
4546                            .as_ref()
4547                    },
4548                    write: |settings_content, value| {
4549                        settings_content
4550                            .project_panel
4551                            .get_or_insert_default()
4552                            .folder_icons = value;
4553                    },
4554                }),
4555                metadata: None,
4556                files: USER,
4557            }),
4558            SettingsPageItem::SettingItem(SettingItem {
4559                title: "Git Status",
4560                description: "Show the Git status in the project panel.",
4561                field: Box::new(SettingField {
4562                    json_path: Some("project_panel.git_status"),
4563                    pick: |settings_content| {
4564                        settings_content.project_panel.as_ref()?.git_status.as_ref()
4565                    },
4566                    write: |settings_content, value| {
4567                        settings_content
4568                            .project_panel
4569                            .get_or_insert_default()
4570                            .git_status = value;
4571                    },
4572                }),
4573                metadata: None,
4574                files: USER,
4575            }),
4576            SettingsPageItem::SettingItem(SettingItem {
4577                title: "Indent Size",
4578                description: "Amount of indentation for nested items.",
4579                field: Box::new(SettingField {
4580                    json_path: Some("project_panel.indent_size"),
4581                    pick: |settings_content| {
4582                        settings_content
4583                            .project_panel
4584                            .as_ref()?
4585                            .indent_size
4586                            .as_ref()
4587                    },
4588                    write: |settings_content, value| {
4589                        settings_content
4590                            .project_panel
4591                            .get_or_insert_default()
4592                            .indent_size = value;
4593                    },
4594                }),
4595                metadata: None,
4596                files: USER,
4597            }),
4598            SettingsPageItem::SettingItem(SettingItem {
4599                title: "Auto Reveal Entries",
4600                description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4601                field: Box::new(SettingField {
4602                    json_path: Some("project_panel.auto_reveal_entries"),
4603                    pick: |settings_content| {
4604                        settings_content
4605                            .project_panel
4606                            .as_ref()?
4607                            .auto_reveal_entries
4608                            .as_ref()
4609                    },
4610                    write: |settings_content, value| {
4611                        settings_content
4612                            .project_panel
4613                            .get_or_insert_default()
4614                            .auto_reveal_entries = value;
4615                    },
4616                }),
4617                metadata: None,
4618                files: USER,
4619            }),
4620            SettingsPageItem::SettingItem(SettingItem {
4621                title: "Starts Open",
4622                description: "Whether the project panel should open on startup.",
4623                field: Box::new(SettingField {
4624                    json_path: Some("project_panel.starts_open"),
4625                    pick: |settings_content| {
4626                        settings_content
4627                            .project_panel
4628                            .as_ref()?
4629                            .starts_open
4630                            .as_ref()
4631                    },
4632                    write: |settings_content, value| {
4633                        settings_content
4634                            .project_panel
4635                            .get_or_insert_default()
4636                            .starts_open = value;
4637                    },
4638                }),
4639                metadata: None,
4640                files: USER,
4641            }),
4642            SettingsPageItem::SettingItem(SettingItem {
4643                title: "Auto Fold Directories",
4644                description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4645                field: Box::new(SettingField {
4646                    json_path: Some("project_panel.auto_fold_dirs"),
4647                    pick: |settings_content| {
4648                        settings_content
4649                            .project_panel
4650                            .as_ref()?
4651                            .auto_fold_dirs
4652                            .as_ref()
4653                    },
4654                    write: |settings_content, value| {
4655                        settings_content
4656                            .project_panel
4657                            .get_or_insert_default()
4658                            .auto_fold_dirs = value;
4659                    },
4660                }),
4661                metadata: None,
4662                files: USER,
4663            }),
4664            SettingsPageItem::SettingItem(SettingItem {
4665                title: "Bold Folder Labels",
4666                description: "Whether to show folder names with bold text in the project panel.",
4667                field: Box::new(SettingField {
4668                    json_path: Some("project_panel.bold_folder_labels"),
4669                    pick: |settings_content| {
4670                        settings_content
4671                            .project_panel
4672                            .as_ref()?
4673                            .bold_folder_labels
4674                            .as_ref()
4675                    },
4676                    write: |settings_content, value| {
4677                        settings_content
4678                            .project_panel
4679                            .get_or_insert_default()
4680                            .bold_folder_labels = value;
4681                    },
4682                }),
4683                metadata: None,
4684                files: USER,
4685            }),
4686            SettingsPageItem::SettingItem(SettingItem {
4687                title: "Show Scrollbar",
4688                description: "Show the scrollbar in the project panel.",
4689                field: Box::new(SettingField {
4690                    json_path: Some("project_panel.scrollbar.show"),
4691                    pick: |settings_content| {
4692                        show_scrollbar_or_editor(settings_content, |settings_content| {
4693                            settings_content
4694                                .project_panel
4695                                .as_ref()?
4696                                .scrollbar
4697                                .as_ref()?
4698                                .show
4699                                .as_ref()
4700                        })
4701                    },
4702                    write: |settings_content, value| {
4703                        settings_content
4704                            .project_panel
4705                            .get_or_insert_default()
4706                            .scrollbar
4707                            .get_or_insert_default()
4708                            .show = value;
4709                    },
4710                }),
4711                metadata: None,
4712                files: USER,
4713            }),
4714            SettingsPageItem::SettingItem(SettingItem {
4715                title: "Horizontal Scroll",
4716                description: "Whether to allow horizontal scrolling in the project panel. When disabled, the view is always locked to the leftmost position and long file names are clipped.",
4717                field: Box::new(SettingField {
4718                    json_path: Some("project_panel.scrollbar.horizontal_scroll"),
4719                    pick: |settings_content| {
4720                        settings_content
4721                            .project_panel
4722                            .as_ref()?
4723                            .scrollbar
4724                            .as_ref()?
4725                            .horizontal_scroll
4726                            .as_ref()
4727                    },
4728                    write: |settings_content, value| {
4729                        settings_content
4730                            .project_panel
4731                            .get_or_insert_default()
4732                            .scrollbar
4733                            .get_or_insert_default()
4734                            .horizontal_scroll = value;
4735                    },
4736                }),
4737                metadata: None,
4738                files: USER,
4739            }),
4740            SettingsPageItem::SettingItem(SettingItem {
4741                title: "Show Diagnostics",
4742                description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4743                field: Box::new(SettingField {
4744                    json_path: Some("project_panel.show_diagnostics"),
4745                    pick: |settings_content| {
4746                        settings_content
4747                            .project_panel
4748                            .as_ref()?
4749                            .show_diagnostics
4750                            .as_ref()
4751                    },
4752                    write: |settings_content, value| {
4753                        settings_content
4754                            .project_panel
4755                            .get_or_insert_default()
4756                            .show_diagnostics = value;
4757                    },
4758                }),
4759                metadata: None,
4760                files: USER,
4761            }),
4762            SettingsPageItem::SettingItem(SettingItem {
4763                title: "Diagnostic Badges",
4764                description: "Show error and warning count badges next to file names in the project panel.",
4765                field: Box::new(SettingField {
4766                    json_path: Some("project_panel.diagnostic_badges"),
4767                    pick: |settings_content| {
4768                        settings_content
4769                            .project_panel
4770                            .as_ref()?
4771                            .diagnostic_badges
4772                            .as_ref()
4773                    },
4774                    write: |settings_content, value| {
4775                        settings_content
4776                            .project_panel
4777                            .get_or_insert_default()
4778                            .diagnostic_badges = value;
4779                    },
4780                }),
4781                metadata: None,
4782                files: USER,
4783            }),
4784            SettingsPageItem::SettingItem(SettingItem {
4785                title: "Git Status Indicator",
4786                description: "Show a git status indicator next to file names in the project panel.",
4787                field: Box::new(SettingField {
4788                    json_path: Some("project_panel.git_status_indicator"),
4789                    pick: |settings_content| {
4790                        settings_content
4791                            .project_panel
4792                            .as_ref()?
4793                            .git_status_indicator
4794                            .as_ref()
4795                    },
4796                    write: |settings_content, value| {
4797                        settings_content
4798                            .project_panel
4799                            .get_or_insert_default()
4800                            .git_status_indicator = value;
4801                    },
4802                }),
4803                metadata: None,
4804                files: USER,
4805            }),
4806            SettingsPageItem::SettingItem(SettingItem {
4807                title: "Sticky Scroll",
4808                description: "Whether to stick parent directories at top of the project panel.",
4809                field: Box::new(SettingField {
4810                    json_path: Some("project_panel.sticky_scroll"),
4811                    pick: |settings_content| {
4812                        settings_content
4813                            .project_panel
4814                            .as_ref()?
4815                            .sticky_scroll
4816                            .as_ref()
4817                    },
4818                    write: |settings_content, value| {
4819                        settings_content
4820                            .project_panel
4821                            .get_or_insert_default()
4822                            .sticky_scroll = value;
4823                    },
4824                }),
4825                metadata: None,
4826                files: USER,
4827            }),
4828            SettingsPageItem::SettingItem(SettingItem {
4829                files: USER,
4830                title: "Show Indent Guides",
4831                description: "Show indent guides in the project panel.",
4832                field: Box::new(SettingField {
4833                    json_path: Some("project_panel.indent_guides.show"),
4834                    pick: |settings_content| {
4835                        settings_content
4836                            .project_panel
4837                            .as_ref()?
4838                            .indent_guides
4839                            .as_ref()?
4840                            .show
4841                            .as_ref()
4842                    },
4843                    write: |settings_content, value| {
4844                        settings_content
4845                            .project_panel
4846                            .get_or_insert_default()
4847                            .indent_guides
4848                            .get_or_insert_default()
4849                            .show = value;
4850                    },
4851                }),
4852                metadata: None,
4853            }),
4854            SettingsPageItem::SettingItem(SettingItem {
4855                title: "Drag and Drop",
4856                description: "Whether to enable drag-and-drop operations in the project panel.",
4857                field: Box::new(SettingField {
4858                    json_path: Some("project_panel.drag_and_drop"),
4859                    pick: |settings_content| {
4860                        settings_content
4861                            .project_panel
4862                            .as_ref()?
4863                            .drag_and_drop
4864                            .as_ref()
4865                    },
4866                    write: |settings_content, value| {
4867                        settings_content
4868                            .project_panel
4869                            .get_or_insert_default()
4870                            .drag_and_drop = value;
4871                    },
4872                }),
4873                metadata: None,
4874                files: USER,
4875            }),
4876            SettingsPageItem::SettingItem(SettingItem {
4877                title: "Hide Root",
4878                description: "Whether to hide the root entry when only one folder is open in the window.",
4879                field: Box::new(SettingField {
4880                    json_path: Some("project_panel.hide_root"),
4881                    pick: |settings_content| {
4882                        settings_content.project_panel.as_ref()?.hide_root.as_ref()
4883                    },
4884                    write: |settings_content, value| {
4885                        settings_content
4886                            .project_panel
4887                            .get_or_insert_default()
4888                            .hide_root = value;
4889                    },
4890                }),
4891                metadata: None,
4892                files: USER,
4893            }),
4894            SettingsPageItem::SettingItem(SettingItem {
4895                title: "Hide Hidden",
4896                description: "Whether to hide the hidden entries in the project panel.",
4897                field: Box::new(SettingField {
4898                    json_path: Some("project_panel.hide_hidden"),
4899                    pick: |settings_content| {
4900                        settings_content
4901                            .project_panel
4902                            .as_ref()?
4903                            .hide_hidden
4904                            .as_ref()
4905                    },
4906                    write: |settings_content, value| {
4907                        settings_content
4908                            .project_panel
4909                            .get_or_insert_default()
4910                            .hide_hidden = value;
4911                    },
4912                }),
4913                metadata: None,
4914                files: USER,
4915            }),
4916            SettingsPageItem::SettingItem(SettingItem {
4917                title: "Hidden Files",
4918                description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4919                field: Box::new(
4920                    SettingField {
4921                        json_path: Some("worktree.hidden_files"),
4922                        pick: |settings_content| {
4923                            settings_content.project.worktree.hidden_files.as_ref()
4924                        },
4925                        write: |settings_content, value| {
4926                            settings_content.project.worktree.hidden_files = value;
4927                        },
4928                    }
4929                    .unimplemented(),
4930                ),
4931                metadata: None,
4932                files: USER,
4933            }),
4934        ]
4935    }
4936
4937    fn auto_open_files_section() -> [SettingsPageItem; 5] {
4938        [
4939            SettingsPageItem::SectionHeader("Auto Open Files"),
4940            SettingsPageItem::SettingItem(SettingItem {
4941                title: "On Create",
4942                description: "Whether to automatically open newly created files in the editor.",
4943                field: Box::new(SettingField {
4944                    json_path: Some("project_panel.auto_open.on_create"),
4945                    pick: |settings_content| {
4946                        settings_content
4947                            .project_panel
4948                            .as_ref()?
4949                            .auto_open
4950                            .as_ref()?
4951                            .on_create
4952                            .as_ref()
4953                    },
4954                    write: |settings_content, value| {
4955                        settings_content
4956                            .project_panel
4957                            .get_or_insert_default()
4958                            .auto_open
4959                            .get_or_insert_default()
4960                            .on_create = value;
4961                    },
4962                }),
4963                metadata: None,
4964                files: USER,
4965            }),
4966            SettingsPageItem::SettingItem(SettingItem {
4967                title: "On Paste",
4968                description: "Whether to automatically open files after pasting or duplicating them.",
4969                field: Box::new(SettingField {
4970                    json_path: Some("project_panel.auto_open.on_paste"),
4971                    pick: |settings_content| {
4972                        settings_content
4973                            .project_panel
4974                            .as_ref()?
4975                            .auto_open
4976                            .as_ref()?
4977                            .on_paste
4978                            .as_ref()
4979                    },
4980                    write: |settings_content, value| {
4981                        settings_content
4982                            .project_panel
4983                            .get_or_insert_default()
4984                            .auto_open
4985                            .get_or_insert_default()
4986                            .on_paste = value;
4987                    },
4988                }),
4989                metadata: None,
4990                files: USER,
4991            }),
4992            SettingsPageItem::SettingItem(SettingItem {
4993                title: "On Drop",
4994                description: "Whether to automatically open files dropped from external sources.",
4995                field: Box::new(SettingField {
4996                    json_path: Some("project_panel.auto_open.on_drop"),
4997                    pick: |settings_content| {
4998                        settings_content
4999                            .project_panel
5000                            .as_ref()?
5001                            .auto_open
5002                            .as_ref()?
5003                            .on_drop
5004                            .as_ref()
5005                    },
5006                    write: |settings_content, value| {
5007                        settings_content
5008                            .project_panel
5009                            .get_or_insert_default()
5010                            .auto_open
5011                            .get_or_insert_default()
5012                            .on_drop = value;
5013                    },
5014                }),
5015                metadata: None,
5016                files: USER,
5017            }),
5018            SettingsPageItem::SettingItem(SettingItem {
5019                title: "Sort Mode",
5020                description: "Sort order for entries in the project panel.",
5021                field: Box::new(SettingField {
5022                    pick: |settings_content| {
5023                        settings_content.project_panel.as_ref()?.sort_mode.as_ref()
5024                    },
5025                    write: |settings_content, value| {
5026                        settings_content
5027                            .project_panel
5028                            .get_or_insert_default()
5029                            .sort_mode = value;
5030                    },
5031                    json_path: Some("project_panel.sort_mode"),
5032                }),
5033                metadata: None,
5034                files: USER,
5035            }),
5036        ]
5037    }
5038
5039    fn terminal_panel_section() -> [SettingsPageItem; 4] {
5040        [
5041            SettingsPageItem::SectionHeader("Terminal Panel"),
5042            SettingsPageItem::SettingItem(SettingItem {
5043                title: "Terminal Dock",
5044                description: "Where to dock the terminal panel.",
5045                field: Box::new(SettingField {
5046                    json_path: Some("terminal.dock"),
5047                    pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
5048                    write: |settings_content, value| {
5049                        settings_content.terminal.get_or_insert_default().dock = value;
5050                    },
5051                }),
5052                metadata: None,
5053                files: USER,
5054            }),
5055            SettingsPageItem::SettingItem(SettingItem {
5056                title: "Terminal Panel Flexible Sizing",
5057                description: "Whether the terminal panel should use flexible (proportional) sizing when docked to the left or right.",
5058                field: Box::new(SettingField {
5059                    json_path: Some("terminal.flexible"),
5060                    pick: |settings_content| settings_content.terminal.as_ref()?.flexible.as_ref(),
5061                    write: |settings_content, value| {
5062                        settings_content.terminal.get_or_insert_default().flexible = value;
5063                    },
5064                }),
5065                metadata: None,
5066                files: USER,
5067            }),
5068            SettingsPageItem::SettingItem(SettingItem {
5069                title: "Show Count Badge",
5070                description: "Show a badge on the terminal panel icon with the count of open terminals.",
5071                field: Box::new(SettingField {
5072                    json_path: Some("terminal.show_count_badge"),
5073                    pick: |settings_content| {
5074                        settings_content
5075                            .terminal
5076                            .as_ref()?
5077                            .show_count_badge
5078                            .as_ref()
5079                    },
5080                    write: |settings_content, value| {
5081                        settings_content
5082                            .terminal
5083                            .get_or_insert_default()
5084                            .show_count_badge = value;
5085                    },
5086                }),
5087                metadata: None,
5088                files: USER,
5089            }),
5090        ]
5091    }
5092
5093    fn outline_panel_section() -> [SettingsPageItem; 11] {
5094        [
5095            SettingsPageItem::SectionHeader("Outline Panel"),
5096            SettingsPageItem::SettingItem(SettingItem {
5097                title: "Outline Panel Button",
5098                description: "Show the outline panel button in the status bar.",
5099                field: Box::new(SettingField {
5100                    json_path: Some("outline_panel.button"),
5101                    pick: |settings_content| {
5102                        settings_content.outline_panel.as_ref()?.button.as_ref()
5103                    },
5104                    write: |settings_content, value| {
5105                        settings_content
5106                            .outline_panel
5107                            .get_or_insert_default()
5108                            .button = value;
5109                    },
5110                }),
5111                metadata: None,
5112                files: USER,
5113            }),
5114            SettingsPageItem::SettingItem(SettingItem {
5115                title: "Outline Panel Dock",
5116                description: "Where to dock the outline panel.",
5117                field: Box::new(SettingField {
5118                    json_path: Some("outline_panel.dock"),
5119                    pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
5120                    write: |settings_content, value| {
5121                        settings_content.outline_panel.get_or_insert_default().dock = value;
5122                    },
5123                }),
5124                metadata: None,
5125                files: USER,
5126            }),
5127            SettingsPageItem::SettingItem(SettingItem {
5128                title: "Outline Panel Default Width",
5129                description: "Default width of the outline panel in pixels.",
5130                field: Box::new(SettingField {
5131                    json_path: Some("outline_panel.default_width"),
5132                    pick: |settings_content| {
5133                        settings_content
5134                            .outline_panel
5135                            .as_ref()?
5136                            .default_width
5137                            .as_ref()
5138                    },
5139                    write: |settings_content, value| {
5140                        settings_content
5141                            .outline_panel
5142                            .get_or_insert_default()
5143                            .default_width = value;
5144                    },
5145                }),
5146                metadata: None,
5147                files: USER,
5148            }),
5149            SettingsPageItem::SettingItem(SettingItem {
5150                title: "File Icons",
5151                description: "Show file icons in the outline panel.",
5152                field: Box::new(SettingField {
5153                    json_path: Some("outline_panel.file_icons"),
5154                    pick: |settings_content| {
5155                        settings_content.outline_panel.as_ref()?.file_icons.as_ref()
5156                    },
5157                    write: |settings_content, value| {
5158                        settings_content
5159                            .outline_panel
5160                            .get_or_insert_default()
5161                            .file_icons = value;
5162                    },
5163                }),
5164                metadata: None,
5165                files: USER,
5166            }),
5167            SettingsPageItem::SettingItem(SettingItem {
5168                title: "Folder Icons",
5169                description: "Whether to show folder icons or chevrons for directories in the outline panel.",
5170                field: Box::new(SettingField {
5171                    json_path: Some("outline_panel.folder_icons"),
5172                    pick: |settings_content| {
5173                        settings_content
5174                            .outline_panel
5175                            .as_ref()?
5176                            .folder_icons
5177                            .as_ref()
5178                    },
5179                    write: |settings_content, value| {
5180                        settings_content
5181                            .outline_panel
5182                            .get_or_insert_default()
5183                            .folder_icons = value;
5184                    },
5185                }),
5186                metadata: None,
5187                files: USER,
5188            }),
5189            SettingsPageItem::SettingItem(SettingItem {
5190                title: "Git Status",
5191                description: "Show the Git status in the outline panel.",
5192                field: Box::new(SettingField {
5193                    json_path: Some("outline_panel.git_status"),
5194                    pick: |settings_content| {
5195                        settings_content.outline_panel.as_ref()?.git_status.as_ref()
5196                    },
5197                    write: |settings_content, value| {
5198                        settings_content
5199                            .outline_panel
5200                            .get_or_insert_default()
5201                            .git_status = value;
5202                    },
5203                }),
5204                metadata: None,
5205                files: USER,
5206            }),
5207            SettingsPageItem::SettingItem(SettingItem {
5208                title: "Indent Size",
5209                description: "Amount of indentation for nested items.",
5210                field: Box::new(SettingField {
5211                    json_path: Some("outline_panel.indent_size"),
5212                    pick: |settings_content| {
5213                        settings_content
5214                            .outline_panel
5215                            .as_ref()?
5216                            .indent_size
5217                            .as_ref()
5218                    },
5219                    write: |settings_content, value| {
5220                        settings_content
5221                            .outline_panel
5222                            .get_or_insert_default()
5223                            .indent_size = value;
5224                    },
5225                }),
5226                metadata: None,
5227                files: USER,
5228            }),
5229            SettingsPageItem::SettingItem(SettingItem {
5230                title: "Auto Reveal Entries",
5231                description: "Whether to reveal when a corresponding outline entry becomes active.",
5232                field: Box::new(SettingField {
5233                    json_path: Some("outline_panel.auto_reveal_entries"),
5234                    pick: |settings_content| {
5235                        settings_content
5236                            .outline_panel
5237                            .as_ref()?
5238                            .auto_reveal_entries
5239                            .as_ref()
5240                    },
5241                    write: |settings_content, value| {
5242                        settings_content
5243                            .outline_panel
5244                            .get_or_insert_default()
5245                            .auto_reveal_entries = value;
5246                    },
5247                }),
5248                metadata: None,
5249                files: USER,
5250            }),
5251            SettingsPageItem::SettingItem(SettingItem {
5252                title: "Auto Fold Directories",
5253                description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
5254                field: Box::new(SettingField {
5255                    json_path: Some("outline_panel.auto_fold_dirs"),
5256                    pick: |settings_content| {
5257                        settings_content
5258                            .outline_panel
5259                            .as_ref()?
5260                            .auto_fold_dirs
5261                            .as_ref()
5262                    },
5263                    write: |settings_content, value| {
5264                        settings_content
5265                            .outline_panel
5266                            .get_or_insert_default()
5267                            .auto_fold_dirs = value;
5268                    },
5269                }),
5270                metadata: None,
5271                files: USER,
5272            }),
5273            SettingsPageItem::SettingItem(SettingItem {
5274                files: USER,
5275                title: "Show Indent Guides",
5276                description: "When to show indent guides in the outline panel.",
5277                field: Box::new(SettingField {
5278                    json_path: Some("outline_panel.indent_guides.show"),
5279                    pick: |settings_content| {
5280                        settings_content
5281                            .outline_panel
5282                            .as_ref()?
5283                            .indent_guides
5284                            .as_ref()?
5285                            .show
5286                            .as_ref()
5287                    },
5288                    write: |settings_content, value| {
5289                        settings_content
5290                            .outline_panel
5291                            .get_or_insert_default()
5292                            .indent_guides
5293                            .get_or_insert_default()
5294                            .show = value;
5295                    },
5296                }),
5297                metadata: None,
5298            }),
5299        ]
5300    }
5301
5302    fn git_panel_section() -> [SettingsPageItem; 14] {
5303        [
5304            SettingsPageItem::SectionHeader("Git Panel"),
5305            SettingsPageItem::SettingItem(SettingItem {
5306                title: "Git Panel Button",
5307                description: "Show the Git panel button in the status bar.",
5308                field: Box::new(SettingField {
5309                    json_path: Some("git_panel.button"),
5310                    pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5311                    write: |settings_content, value| {
5312                        settings_content.git_panel.get_or_insert_default().button = value;
5313                    },
5314                }),
5315                metadata: None,
5316                files: USER,
5317            }),
5318            SettingsPageItem::SettingItem(SettingItem {
5319                title: "Git Panel Dock",
5320                description: "Where to dock the Git panel.",
5321                field: Box::new(SettingField {
5322                    json_path: Some("git_panel.dock"),
5323                    pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5324                    write: |settings_content, value| {
5325                        settings_content.git_panel.get_or_insert_default().dock = value;
5326                    },
5327                }),
5328                metadata: None,
5329                files: USER,
5330            }),
5331            SettingsPageItem::SettingItem(SettingItem {
5332                title: "Git Panel Default Width",
5333                description: "Default width of the Git panel in pixels.",
5334                field: Box::new(SettingField {
5335                    json_path: Some("git_panel.default_width"),
5336                    pick: |settings_content| {
5337                        settings_content.git_panel.as_ref()?.default_width.as_ref()
5338                    },
5339                    write: |settings_content, value| {
5340                        settings_content
5341                            .git_panel
5342                            .get_or_insert_default()
5343                            .default_width = value;
5344                    },
5345                }),
5346                metadata: None,
5347                files: USER,
5348            }),
5349            SettingsPageItem::SettingItem(SettingItem {
5350                title: "Git Panel Status Style",
5351                description: "How entry statuses are displayed.",
5352                field: Box::new(SettingField {
5353                    json_path: Some("git_panel.status_style"),
5354                    pick: |settings_content| {
5355                        settings_content.git_panel.as_ref()?.status_style.as_ref()
5356                    },
5357                    write: |settings_content, value| {
5358                        settings_content
5359                            .git_panel
5360                            .get_or_insert_default()
5361                            .status_style = value;
5362                    },
5363                }),
5364                metadata: None,
5365                files: USER,
5366            }),
5367            SettingsPageItem::SettingItem(SettingItem {
5368                title: "Fallback Branch Name",
5369                description: "Default branch name will be when init.defaultbranch is not set in Git.",
5370                field: Box::new(SettingField {
5371                    json_path: Some("git_panel.fallback_branch_name"),
5372                    pick: |settings_content| {
5373                        settings_content
5374                            .git_panel
5375                            .as_ref()?
5376                            .fallback_branch_name
5377                            .as_ref()
5378                    },
5379                    write: |settings_content, value| {
5380                        settings_content
5381                            .git_panel
5382                            .get_or_insert_default()
5383                            .fallback_branch_name = value;
5384                    },
5385                }),
5386                metadata: None,
5387                files: USER,
5388            }),
5389            SettingsPageItem::SettingItem(SettingItem {
5390                title: "Sort By Path",
5391                description: "Enable to sort entries in the panel by path, disable to sort by status.",
5392                field: Box::new(SettingField {
5393                    json_path: Some("git_panel.sort_by_path"),
5394                    pick: |settings_content| {
5395                        settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5396                    },
5397                    write: |settings_content, value| {
5398                        settings_content
5399                            .git_panel
5400                            .get_or_insert_default()
5401                            .sort_by_path = value;
5402                    },
5403                }),
5404                metadata: None,
5405                files: USER,
5406            }),
5407            SettingsPageItem::SettingItem(SettingItem {
5408                title: "Collapse Untracked Diff",
5409                description: "Whether to collapse untracked files in the diff panel.",
5410                field: Box::new(SettingField {
5411                    json_path: Some("git_panel.collapse_untracked_diff"),
5412                    pick: |settings_content| {
5413                        settings_content
5414                            .git_panel
5415                            .as_ref()?
5416                            .collapse_untracked_diff
5417                            .as_ref()
5418                    },
5419                    write: |settings_content, value| {
5420                        settings_content
5421                            .git_panel
5422                            .get_or_insert_default()
5423                            .collapse_untracked_diff = value;
5424                    },
5425                }),
5426                metadata: None,
5427                files: USER,
5428            }),
5429            SettingsPageItem::SettingItem(SettingItem {
5430                title: "Tree View",
5431                description: "Enable to show entries in tree view list, disable to show in flat view list.",
5432                field: Box::new(SettingField {
5433                    json_path: Some("git_panel.tree_view"),
5434                    pick: |settings_content| {
5435                        settings_content.git_panel.as_ref()?.tree_view.as_ref()
5436                    },
5437                    write: |settings_content, value| {
5438                        settings_content.git_panel.get_or_insert_default().tree_view = value;
5439                    },
5440                }),
5441                metadata: None,
5442                files: USER,
5443            }),
5444            SettingsPageItem::SettingItem(SettingItem {
5445                title: "File Icons",
5446                description: "Show file icons next to the Git status icon.",
5447                field: Box::new(SettingField {
5448                    json_path: Some("git_panel.file_icons"),
5449                    pick: |settings_content| {
5450                        settings_content.git_panel.as_ref()?.file_icons.as_ref()
5451                    },
5452                    write: |settings_content, value| {
5453                        settings_content
5454                            .git_panel
5455                            .get_or_insert_default()
5456                            .file_icons = value;
5457                    },
5458                }),
5459                metadata: None,
5460                files: USER,
5461            }),
5462            SettingsPageItem::SettingItem(SettingItem {
5463                title: "Folder Icons",
5464                description: "Whether to show folder icons or chevrons for directories in the git panel.",
5465                field: Box::new(SettingField {
5466                    json_path: Some("git_panel.folder_icons"),
5467                    pick: |settings_content| {
5468                        settings_content.git_panel.as_ref()?.folder_icons.as_ref()
5469                    },
5470                    write: |settings_content, value| {
5471                        settings_content
5472                            .git_panel
5473                            .get_or_insert_default()
5474                            .folder_icons = value;
5475                    },
5476                }),
5477                metadata: None,
5478                files: USER,
5479            }),
5480            SettingsPageItem::SettingItem(SettingItem {
5481                title: "Diff Stats",
5482                description: "Whether to show the addition/deletion change count next to each file in the Git panel.",
5483                field: Box::new(SettingField {
5484                    json_path: Some("git_panel.diff_stats"),
5485                    pick: |settings_content| {
5486                        settings_content.git_panel.as_ref()?.diff_stats.as_ref()
5487                    },
5488                    write: |settings_content, value| {
5489                        settings_content
5490                            .git_panel
5491                            .get_or_insert_default()
5492                            .diff_stats = value;
5493                    },
5494                }),
5495                metadata: None,
5496                files: USER,
5497            }),
5498            SettingsPageItem::SettingItem(SettingItem {
5499                title: "Show Count Badge",
5500                description: "Whether to show a badge on the git panel icon with the count of uncommitted changes.",
5501                field: Box::new(SettingField {
5502                    json_path: Some("git_panel.show_count_badge"),
5503                    pick: |settings_content| {
5504                        settings_content
5505                            .git_panel
5506                            .as_ref()?
5507                            .show_count_badge
5508                            .as_ref()
5509                    },
5510                    write: |settings_content, value| {
5511                        settings_content
5512                            .git_panel
5513                            .get_or_insert_default()
5514                            .show_count_badge = value;
5515                    },
5516                }),
5517                metadata: None,
5518                files: USER,
5519            }),
5520            SettingsPageItem::SettingItem(SettingItem {
5521                title: "Scroll Bar",
5522                description: "How and when the scrollbar should be displayed.",
5523                field: Box::new(SettingField {
5524                    json_path: Some("git_panel.scrollbar.show"),
5525                    pick: |settings_content| {
5526                        show_scrollbar_or_editor(settings_content, |settings_content| {
5527                            settings_content
5528                                .git_panel
5529                                .as_ref()?
5530                                .scrollbar
5531                                .as_ref()?
5532                                .show
5533                                .as_ref()
5534                        })
5535                    },
5536                    write: |settings_content, value| {
5537                        settings_content
5538                            .git_panel
5539                            .get_or_insert_default()
5540                            .scrollbar
5541                            .get_or_insert_default()
5542                            .show = value;
5543                    },
5544                }),
5545                metadata: None,
5546                files: USER,
5547            }),
5548        ]
5549    }
5550
5551    fn debugger_panel_section() -> [SettingsPageItem; 2] {
5552        [
5553            SettingsPageItem::SectionHeader("Debugger Panel"),
5554            SettingsPageItem::SettingItem(SettingItem {
5555                title: "Debugger Panel Dock",
5556                description: "The dock position of the debug panel.",
5557                field: Box::new(SettingField {
5558                    json_path: Some("debugger.dock"),
5559                    pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5560                    write: |settings_content, value| {
5561                        settings_content.debugger.get_or_insert_default().dock = value;
5562                    },
5563                }),
5564                metadata: None,
5565                files: USER,
5566            }),
5567        ]
5568    }
5569
5570    fn notification_panel_section() -> [SettingsPageItem; 5] {
5571        [
5572            SettingsPageItem::SectionHeader("Notification Panel"),
5573            SettingsPageItem::SettingItem(SettingItem {
5574                title: "Notification Panel Button",
5575                description: "Show the notification panel button in the status bar.",
5576                field: Box::new(SettingField {
5577                    json_path: Some("notification_panel.button"),
5578                    pick: |settings_content| {
5579                        settings_content
5580                            .notification_panel
5581                            .as_ref()?
5582                            .button
5583                            .as_ref()
5584                    },
5585                    write: |settings_content, value| {
5586                        settings_content
5587                            .notification_panel
5588                            .get_or_insert_default()
5589                            .button = value;
5590                    },
5591                }),
5592                metadata: None,
5593                files: USER,
5594            }),
5595            SettingsPageItem::SettingItem(SettingItem {
5596                title: "Notification Panel Dock",
5597                description: "Where to dock the notification panel.",
5598                field: Box::new(SettingField {
5599                    json_path: Some("notification_panel.dock"),
5600                    pick: |settings_content| {
5601                        settings_content.notification_panel.as_ref()?.dock.as_ref()
5602                    },
5603                    write: |settings_content, value| {
5604                        settings_content
5605                            .notification_panel
5606                            .get_or_insert_default()
5607                            .dock = value;
5608                    },
5609                }),
5610                metadata: None,
5611                files: USER,
5612            }),
5613            SettingsPageItem::SettingItem(SettingItem {
5614                title: "Notification Panel Default Width",
5615                description: "Default width of the notification panel in pixels.",
5616                field: Box::new(SettingField {
5617                    json_path: Some("notification_panel.default_width"),
5618                    pick: |settings_content| {
5619                        settings_content
5620                            .notification_panel
5621                            .as_ref()?
5622                            .default_width
5623                            .as_ref()
5624                    },
5625                    write: |settings_content, value| {
5626                        settings_content
5627                            .notification_panel
5628                            .get_or_insert_default()
5629                            .default_width = value;
5630                    },
5631                }),
5632                metadata: None,
5633                files: USER,
5634            }),
5635            SettingsPageItem::SettingItem(SettingItem {
5636                title: "Show Count Badge",
5637                description: "Show a badge on the notification panel icon with the count of unread notifications.",
5638                field: Box::new(SettingField {
5639                    json_path: Some("notification_panel.show_count_badge"),
5640                    pick: |settings_content| {
5641                        settings_content
5642                            .notification_panel
5643                            .as_ref()?
5644                            .show_count_badge
5645                            .as_ref()
5646                    },
5647                    write: |settings_content, value| {
5648                        settings_content
5649                            .notification_panel
5650                            .get_or_insert_default()
5651                            .show_count_badge = value;
5652                    },
5653                }),
5654                metadata: None,
5655                files: USER,
5656            }),
5657        ]
5658    }
5659
5660    fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5661        [
5662            SettingsPageItem::SectionHeader("Collaboration Panel"),
5663            SettingsPageItem::SettingItem(SettingItem {
5664                title: "Collaboration Panel Button",
5665                description: "Show the collaboration panel button in the status bar.",
5666                field: Box::new(SettingField {
5667                    json_path: Some("collaboration_panel.button"),
5668                    pick: |settings_content| {
5669                        settings_content
5670                            .collaboration_panel
5671                            .as_ref()?
5672                            .button
5673                            .as_ref()
5674                    },
5675                    write: |settings_content, value| {
5676                        settings_content
5677                            .collaboration_panel
5678                            .get_or_insert_default()
5679                            .button = value;
5680                    },
5681                }),
5682                metadata: None,
5683                files: USER,
5684            }),
5685            SettingsPageItem::SettingItem(SettingItem {
5686                title: "Collaboration Panel Dock",
5687                description: "Where to dock the collaboration panel.",
5688                field: Box::new(SettingField {
5689                    json_path: Some("collaboration_panel.dock"),
5690                    pick: |settings_content| {
5691                        settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5692                    },
5693                    write: |settings_content, value| {
5694                        settings_content
5695                            .collaboration_panel
5696                            .get_or_insert_default()
5697                            .dock = value;
5698                    },
5699                }),
5700                metadata: None,
5701                files: USER,
5702            }),
5703            SettingsPageItem::SettingItem(SettingItem {
5704                title: "Collaboration Panel Default Width",
5705                description: "Default width of the collaboration panel in pixels.",
5706                field: Box::new(SettingField {
5707                    json_path: Some("collaboration_panel.dock"),
5708                    pick: |settings_content| {
5709                        settings_content
5710                            .collaboration_panel
5711                            .as_ref()?
5712                            .default_width
5713                            .as_ref()
5714                    },
5715                    write: |settings_content, value| {
5716                        settings_content
5717                            .collaboration_panel
5718                            .get_or_insert_default()
5719                            .default_width = value;
5720                    },
5721                }),
5722                metadata: None,
5723                files: USER,
5724            }),
5725        ]
5726    }
5727
5728    fn agent_panel_section() -> [SettingsPageItem; 6] {
5729        [
5730            SettingsPageItem::SectionHeader("Agent Panel"),
5731            SettingsPageItem::SettingItem(SettingItem {
5732                title: "Agent Panel Button",
5733                description: "Whether to show the agent panel button in the status bar.",
5734                field: Box::new(SettingField {
5735                    json_path: Some("agent.button"),
5736                    pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5737                    write: |settings_content, value| {
5738                        settings_content.agent.get_or_insert_default().button = value;
5739                    },
5740                }),
5741                metadata: None,
5742                files: USER,
5743            }),
5744            SettingsPageItem::SettingItem(SettingItem {
5745                title: "Agent Panel Dock",
5746                description: "Where to dock the agent panel.",
5747                field: Box::new(SettingField {
5748                    json_path: Some("agent.dock"),
5749                    pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5750                    write: |settings_content, value| {
5751                        settings_content.agent.get_or_insert_default().dock = value;
5752                    },
5753                }),
5754                metadata: None,
5755                files: USER,
5756            }),
5757            SettingsPageItem::SettingItem(SettingItem {
5758                title: "Agent Panel Flexible Sizing",
5759                description: "Whether the agent panel should use flexible (proportional) sizing when docked to the left or right.",
5760                field: Box::new(SettingField {
5761                    json_path: Some("agent.flexible"),
5762                    pick: |settings_content| settings_content.agent.as_ref()?.flexible.as_ref(),
5763                    write: |settings_content, value| {
5764                        settings_content.agent.get_or_insert_default().flexible = value;
5765                    },
5766                }),
5767                metadata: None,
5768                files: USER,
5769            }),
5770            SettingsPageItem::SettingItem(SettingItem {
5771                title: "Agent Panel Default Width",
5772                description: "Default width when the agent panel is docked to the left or right.",
5773                field: Box::new(SettingField {
5774                    json_path: Some("agent.default_width"),
5775                    pick: |settings_content| {
5776                        settings_content.agent.as_ref()?.default_width.as_ref()
5777                    },
5778                    write: |settings_content, value| {
5779                        settings_content.agent.get_or_insert_default().default_width = value;
5780                    },
5781                }),
5782                metadata: None,
5783                files: USER,
5784            }),
5785            SettingsPageItem::SettingItem(SettingItem {
5786                title: "Agent Panel Default Height",
5787                description: "Default height when the agent panel is docked to the bottom.",
5788                field: Box::new(SettingField {
5789                    json_path: Some("agent.default_height"),
5790                    pick: |settings_content| {
5791                        settings_content.agent.as_ref()?.default_height.as_ref()
5792                    },
5793                    write: |settings_content, value| {
5794                        settings_content
5795                            .agent
5796                            .get_or_insert_default()
5797                            .default_height = value;
5798                    },
5799                }),
5800                metadata: None,
5801                files: USER,
5802            }),
5803        ]
5804    }
5805
5806    SettingsPage {
5807        title: "Panels",
5808        items: concat_sections![
5809            project_panel_section(),
5810            auto_open_files_section(),
5811            terminal_panel_section(),
5812            outline_panel_section(),
5813            git_panel_section(),
5814            debugger_panel_section(),
5815            notification_panel_section(),
5816            collaboration_panel_section(),
5817            agent_panel_section(),
5818        ],
5819    }
5820}
5821
5822fn debugger_page() -> SettingsPage {
5823    fn general_section() -> [SettingsPageItem; 6] {
5824        [
5825            SettingsPageItem::SectionHeader("General"),
5826            SettingsPageItem::SettingItem(SettingItem {
5827                title: "Stepping Granularity",
5828                description: "Determines the stepping granularity for debug operations.",
5829                field: Box::new(SettingField {
5830                    json_path: Some("debugger.stepping_granularity"),
5831                    pick: |settings_content| {
5832                        settings_content
5833                            .debugger
5834                            .as_ref()?
5835                            .stepping_granularity
5836                            .as_ref()
5837                    },
5838                    write: |settings_content, value| {
5839                        settings_content
5840                            .debugger
5841                            .get_or_insert_default()
5842                            .stepping_granularity = value;
5843                    },
5844                }),
5845                metadata: None,
5846                files: USER,
5847            }),
5848            SettingsPageItem::SettingItem(SettingItem {
5849                title: "Save Breakpoints",
5850                description: "Whether breakpoints should be reused across Zed sessions.",
5851                field: Box::new(SettingField {
5852                    json_path: Some("debugger.save_breakpoints"),
5853                    pick: |settings_content| {
5854                        settings_content
5855                            .debugger
5856                            .as_ref()?
5857                            .save_breakpoints
5858                            .as_ref()
5859                    },
5860                    write: |settings_content, value| {
5861                        settings_content
5862                            .debugger
5863                            .get_or_insert_default()
5864                            .save_breakpoints = value;
5865                    },
5866                }),
5867                metadata: None,
5868                files: USER,
5869            }),
5870            SettingsPageItem::SettingItem(SettingItem {
5871                title: "Timeout",
5872                description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5873                field: Box::new(SettingField {
5874                    json_path: Some("debugger.timeout"),
5875                    pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5876                    write: |settings_content, value| {
5877                        settings_content.debugger.get_or_insert_default().timeout = value;
5878                    },
5879                }),
5880                metadata: None,
5881                files: USER,
5882            }),
5883            SettingsPageItem::SettingItem(SettingItem {
5884                title: "Log DAP Communications",
5885                description: "Whether to log messages between active debug adapters and Zed.",
5886                field: Box::new(SettingField {
5887                    json_path: Some("debugger.log_dap_communications"),
5888                    pick: |settings_content| {
5889                        settings_content
5890                            .debugger
5891                            .as_ref()?
5892                            .log_dap_communications
5893                            .as_ref()
5894                    },
5895                    write: |settings_content, value| {
5896                        settings_content
5897                            .debugger
5898                            .get_or_insert_default()
5899                            .log_dap_communications = value;
5900                    },
5901                }),
5902                metadata: None,
5903                files: USER,
5904            }),
5905            SettingsPageItem::SettingItem(SettingItem {
5906                title: "Format DAP Log Messages",
5907                description: "Whether to format DAP messages when adding them to debug adapter logger.",
5908                field: Box::new(SettingField {
5909                    json_path: Some("debugger.format_dap_log_messages"),
5910                    pick: |settings_content| {
5911                        settings_content
5912                            .debugger
5913                            .as_ref()?
5914                            .format_dap_log_messages
5915                            .as_ref()
5916                    },
5917                    write: |settings_content, value| {
5918                        settings_content
5919                            .debugger
5920                            .get_or_insert_default()
5921                            .format_dap_log_messages = value;
5922                    },
5923                }),
5924                metadata: None,
5925                files: USER,
5926            }),
5927        ]
5928    }
5929
5930    SettingsPage {
5931        title: "Debugger",
5932        items: concat_sections![general_section()],
5933    }
5934}
5935
5936fn terminal_page() -> SettingsPage {
5937    fn environment_section() -> [SettingsPageItem; 5] {
5938        [
5939                SettingsPageItem::SectionHeader("Environment"),
5940                SettingsPageItem::DynamicItem(DynamicItem {
5941                    discriminant: SettingItem {
5942                        files: USER | PROJECT,
5943                        title: "Shell",
5944                        description: "What shell to use when opening a terminal.",
5945                        field: Box::new(SettingField {
5946                            json_path: Some("terminal.shell$"),
5947                            pick: |settings_content| {
5948                                Some(&dynamic_variants::<settings::Shell>()[
5949                                    settings_content
5950                                        .terminal
5951                                        .as_ref()?
5952                                        .project
5953                                        .shell
5954                                        .as_ref()?
5955                                        .discriminant() as usize
5956                                ])
5957                            },
5958                            write: |settings_content, value| {
5959                                let Some(value) = value else {
5960                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5961                                        terminal.project.shell = None;
5962                                    }
5963                                    return;
5964                                };
5965                                let settings_value = settings_content
5966                                    .terminal
5967                                    .get_or_insert_default()
5968                                    .project
5969                                    .shell
5970                                    .get_or_insert_with(|| settings::Shell::default());
5971                                let default_shell = if cfg!(target_os = "windows") {
5972                                    "powershell.exe"
5973                                } else {
5974                                    "sh"
5975                                };
5976                                *settings_value = match value {
5977                                    settings::ShellDiscriminants::System => settings::Shell::System,
5978                                    settings::ShellDiscriminants::Program => {
5979                                        let program = match settings_value {
5980                                            settings::Shell::Program(program) => program.clone(),
5981                                            settings::Shell::WithArguments { program, .. } => program.clone(),
5982                                            _ => String::from(default_shell),
5983                                        };
5984                                        settings::Shell::Program(program)
5985                                    }
5986                                    settings::ShellDiscriminants::WithArguments => {
5987                                        let (program, args, title_override) = match settings_value {
5988                                            settings::Shell::Program(program) => (program.clone(), vec![], None),
5989                                            settings::Shell::WithArguments {
5990                                                program,
5991                                                args,
5992                                                title_override,
5993                                            } => (program.clone(), args.clone(), title_override.clone()),
5994                                            _ => (String::from(default_shell), vec![], None),
5995                                        };
5996                                        settings::Shell::WithArguments {
5997                                            program,
5998                                            args,
5999                                            title_override,
6000                                        }
6001                                    }
6002                                };
6003                            },
6004                        }),
6005                        metadata: None,
6006                    },
6007                    pick_discriminant: |settings_content| {
6008                        Some(
6009                            settings_content
6010                                .terminal
6011                                .as_ref()?
6012                                .project
6013                                .shell
6014                                .as_ref()?
6015                                .discriminant() as usize,
6016                        )
6017                    },
6018                    fields: dynamic_variants::<settings::Shell>()
6019                        .into_iter()
6020                        .map(|variant| match variant {
6021                            settings::ShellDiscriminants::System => vec![],
6022                            settings::ShellDiscriminants::Program => vec![SettingItem {
6023                                files: USER | PROJECT,
6024                                title: "Program",
6025                                description: "The shell program to use.",
6026                                field: Box::new(SettingField {
6027                                    json_path: Some("terminal.shell"),
6028                                    pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
6029                                    {
6030                                        Some(settings::Shell::Program(program)) => Some(program),
6031                                        _ => None,
6032                                    },
6033                                    write: |settings_content, value| {
6034                                        let Some(value) = value else {
6035                                            return;
6036                                        };
6037                                        match settings_content
6038                                            .terminal
6039                                            .get_or_insert_default()
6040                                            .project
6041                                            .shell
6042                                            .as_mut()
6043                                        {
6044                                            Some(settings::Shell::Program(program)) => *program = value,
6045                                            _ => return,
6046                                        }
6047                                    },
6048                                }),
6049                                metadata: None,
6050                            }],
6051                            settings::ShellDiscriminants::WithArguments => vec![
6052                                SettingItem {
6053                                    files: USER | PROJECT,
6054                                    title: "Program",
6055                                    description: "The shell program to run.",
6056                                    field: Box::new(SettingField {
6057                                        json_path: Some("terminal.shell.program"),
6058                                        pick: |settings_content| {
6059                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6060                                                Some(settings::Shell::WithArguments { program, .. }) => Some(program),
6061                                                _ => None,
6062                                            }
6063                                        },
6064                                        write: |settings_content, value| {
6065                                            let Some(value) = value else {
6066                                                return;
6067                                            };
6068                                            match settings_content
6069                                                .terminal
6070                                                .get_or_insert_default()
6071                                                .project
6072                                                .shell
6073                                                .as_mut()
6074                                            {
6075                                                Some(settings::Shell::WithArguments { program, .. }) => {
6076                                                    *program = value
6077                                                }
6078                                                _ => return,
6079                                            }
6080                                        },
6081                                    }),
6082                                    metadata: None,
6083                                },
6084                                SettingItem {
6085                                    files: USER | PROJECT,
6086                                    title: "Arguments",
6087                                    description: "The arguments to pass to the shell program.",
6088                                    field: Box::new(
6089                                        SettingField {
6090                                            json_path: Some("terminal.shell.args"),
6091                                            pick: |settings_content| {
6092                                                match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6093                                                    Some(settings::Shell::WithArguments { args, .. }) => Some(args),
6094                                                    _ => None,
6095                                                }
6096                                            },
6097                                            write: |settings_content, value| {
6098                                                let Some(value) = value else {
6099                                                    return;
6100                                                };
6101                                                match settings_content
6102                                                    .terminal
6103                                                    .get_or_insert_default()
6104                                                    .project
6105                                                    .shell
6106                                                    .as_mut()
6107                                                {
6108                                                    Some(settings::Shell::WithArguments { args, .. }) => *args = value,
6109                                                    _ => return,
6110                                                }
6111                                            },
6112                                        }
6113                                        .unimplemented(),
6114                                    ),
6115                                    metadata: None,
6116                                },
6117                                SettingItem {
6118                                    files: USER | PROJECT,
6119                                    title: "Title Override",
6120                                    description: "An optional string to override the title of the terminal tab.",
6121                                    field: Box::new(SettingField {
6122                                        json_path: Some("terminal.shell.title_override"),
6123                                        pick: |settings_content| {
6124                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6125                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
6126                                                    title_override.as_ref().or(DEFAULT_EMPTY_STRING)
6127                                                }
6128                                                _ => None,
6129                                            }
6130                                        },
6131                                        write: |settings_content, value| {
6132                                            match settings_content
6133                                                .terminal
6134                                                .get_or_insert_default()
6135                                                .project
6136                                                .shell
6137                                                .as_mut()
6138                                            {
6139                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
6140                                                    *title_override = value.filter(|s| !s.is_empty())
6141                                                }
6142                                                _ => return,
6143                                            }
6144                                        },
6145                                    }),
6146                                    metadata: None,
6147                                },
6148                            ],
6149                        })
6150                        .collect(),
6151                }),
6152                SettingsPageItem::DynamicItem(DynamicItem {
6153                    discriminant: SettingItem {
6154                        files: USER | PROJECT,
6155                        title: "Working Directory",
6156                        description: "What working directory to use when launching the terminal.",
6157                        field: Box::new(SettingField {
6158                            json_path: Some("terminal.working_directory$"),
6159                            pick: |settings_content| {
6160                                Some(&dynamic_variants::<settings::WorkingDirectory>()[
6161                                    settings_content
6162                                        .terminal
6163                                        .as_ref()?
6164                                        .project
6165                                        .working_directory
6166                                        .as_ref()?
6167                                        .discriminant() as usize
6168                                ])
6169                            },
6170                            write: |settings_content, value| {
6171                                let Some(value) = value else {
6172                                    if let Some(terminal) = settings_content.terminal.as_mut() {
6173                                        terminal.project.working_directory = None;
6174                                    }
6175                                    return;
6176                                };
6177                                let settings_value = settings_content
6178                                    .terminal
6179                                    .get_or_insert_default()
6180                                    .project
6181                                    .working_directory
6182                                    .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
6183                                *settings_value = match value {
6184                                    settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
6185                                        settings::WorkingDirectory::CurrentFileDirectory
6186                                    },
6187                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
6188                                        settings::WorkingDirectory::CurrentProjectDirectory
6189                                    }
6190                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
6191                                        settings::WorkingDirectory::FirstProjectDirectory
6192                                    }
6193                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
6194                                        settings::WorkingDirectory::AlwaysHome
6195                                    }
6196                                    settings::WorkingDirectoryDiscriminants::Always => {
6197                                        let directory = match settings_value {
6198                                            settings::WorkingDirectory::Always { .. } => return,
6199                                            _ => String::new(),
6200                                        };
6201                                        settings::WorkingDirectory::Always { directory }
6202                                    }
6203                                };
6204                            },
6205                        }),
6206                        metadata: None,
6207                    },
6208                    pick_discriminant: |settings_content| {
6209                        Some(
6210                            settings_content
6211                                .terminal
6212                                .as_ref()?
6213                                .project
6214                                .working_directory
6215                                .as_ref()?
6216                                .discriminant() as usize,
6217                        )
6218                    },
6219                    fields: dynamic_variants::<settings::WorkingDirectory>()
6220                        .into_iter()
6221                        .map(|variant| match variant {
6222                            settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
6223                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
6224                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
6225                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
6226                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
6227                                files: USER | PROJECT,
6228                                title: "Directory",
6229                                description: "The directory path to use (will be shell expanded).",
6230                                field: Box::new(SettingField {
6231                                    json_path: Some("terminal.working_directory.always"),
6232                                    pick: |settings_content| {
6233                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
6234                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
6235                                            _ => None,
6236                                        }
6237                                    },
6238                                    write: |settings_content, value| {
6239                                        let value = value.unwrap_or_default();
6240                                        match settings_content
6241                                            .terminal
6242                                            .get_or_insert_default()
6243                                            .project
6244                                            .working_directory
6245                                            .as_mut()
6246                                        {
6247                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
6248                                            _ => return,
6249                                        }
6250                                    },
6251                                }),
6252                                metadata: None,
6253                            }],
6254                        })
6255                        .collect(),
6256                }),
6257                SettingsPageItem::SettingItem(SettingItem {
6258                    title: "Environment Variables",
6259                    description: "Key-value pairs to add to the terminal's environment.",
6260                    field: Box::new(
6261                        SettingField {
6262                            json_path: Some("terminal.env"),
6263                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
6264                            write: |settings_content, value| {
6265                                settings_content.terminal.get_or_insert_default().project.env = value;
6266                            },
6267                        }
6268                        .unimplemented(),
6269                    ),
6270                    metadata: None,
6271                    files: USER | PROJECT,
6272                }),
6273                SettingsPageItem::SettingItem(SettingItem {
6274                    title: "Detect Virtual Environment",
6275                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
6276                    field: Box::new(
6277                        SettingField {
6278                            json_path: Some("terminal.detect_venv"),
6279                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
6280                            write: |settings_content, value| {
6281                                settings_content
6282                                    .terminal
6283                                    .get_or_insert_default()
6284                                    .project
6285                                    .detect_venv = value;
6286                            },
6287                        }
6288                        .unimplemented(),
6289                    ),
6290                    metadata: None,
6291                    files: USER | PROJECT,
6292                }),
6293            ]
6294    }
6295
6296    fn font_section() -> [SettingsPageItem; 6] {
6297        [
6298            SettingsPageItem::SectionHeader("Font"),
6299            SettingsPageItem::SettingItem(SettingItem {
6300                title: "Font Size",
6301                description: "Font size for terminal text. If not set, defaults to buffer font size.",
6302                field: Box::new(SettingField {
6303                    json_path: Some("terminal.font_size"),
6304                    pick: |settings_content| {
6305                        settings_content
6306                            .terminal
6307                            .as_ref()
6308                            .and_then(|terminal| terminal.font_size.as_ref())
6309                            .or(settings_content.theme.buffer_font_size.as_ref())
6310                    },
6311                    write: |settings_content, value| {
6312                        settings_content.terminal.get_or_insert_default().font_size = value;
6313                    },
6314                }),
6315                metadata: None,
6316                files: USER,
6317            }),
6318            SettingsPageItem::SettingItem(SettingItem {
6319                title: "Font Family",
6320                description: "Font family for terminal text. If not set, defaults to buffer font family.",
6321                field: Box::new(SettingField {
6322                    json_path: Some("terminal.font_family"),
6323                    pick: |settings_content| {
6324                        settings_content
6325                            .terminal
6326                            .as_ref()
6327                            .and_then(|terminal| terminal.font_family.as_ref())
6328                            .or(settings_content.theme.buffer_font_family.as_ref())
6329                    },
6330                    write: |settings_content, value| {
6331                        settings_content
6332                            .terminal
6333                            .get_or_insert_default()
6334                            .font_family = value;
6335                    },
6336                }),
6337                metadata: None,
6338                files: USER,
6339            }),
6340            SettingsPageItem::SettingItem(SettingItem {
6341                title: "Font Fallbacks",
6342                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
6343                field: Box::new(
6344                    SettingField {
6345                        json_path: Some("terminal.font_fallbacks"),
6346                        pick: |settings_content| {
6347                            settings_content
6348                                .terminal
6349                                .as_ref()
6350                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
6351                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
6352                        },
6353                        write: |settings_content, value| {
6354                            settings_content
6355                                .terminal
6356                                .get_or_insert_default()
6357                                .font_fallbacks = value;
6358                        },
6359                    }
6360                    .unimplemented(),
6361                ),
6362                metadata: None,
6363                files: USER,
6364            }),
6365            SettingsPageItem::SettingItem(SettingItem {
6366                title: "Font Weight",
6367                description: "Font weight for terminal text in CSS weight units (100-900).",
6368                field: Box::new(SettingField {
6369                    json_path: Some("terminal.font_weight"),
6370                    pick: |settings_content| {
6371                        settings_content.terminal.as_ref()?.font_weight.as_ref()
6372                    },
6373                    write: |settings_content, value| {
6374                        settings_content
6375                            .terminal
6376                            .get_or_insert_default()
6377                            .font_weight = value;
6378                    },
6379                }),
6380                metadata: None,
6381                files: USER,
6382            }),
6383            SettingsPageItem::SettingItem(SettingItem {
6384                title: "Font Features",
6385                description: "Font features for terminal text.",
6386                field: Box::new(
6387                    SettingField {
6388                        json_path: Some("terminal.font_features"),
6389                        pick: |settings_content| {
6390                            settings_content
6391                                .terminal
6392                                .as_ref()
6393                                .and_then(|terminal| terminal.font_features.as_ref())
6394                                .or(settings_content.theme.buffer_font_features.as_ref())
6395                        },
6396                        write: |settings_content, value| {
6397                            settings_content
6398                                .terminal
6399                                .get_or_insert_default()
6400                                .font_features = value;
6401                        },
6402                    }
6403                    .unimplemented(),
6404                ),
6405                metadata: None,
6406                files: USER,
6407            }),
6408        ]
6409    }
6410
6411    fn display_settings_section() -> [SettingsPageItem; 6] {
6412        [
6413            SettingsPageItem::SectionHeader("Display Settings"),
6414            SettingsPageItem::SettingItem(SettingItem {
6415                title: "Line Height",
6416                description: "Line height for terminal text.",
6417                field: Box::new(
6418                    SettingField {
6419                        json_path: Some("terminal.line_height"),
6420                        pick: |settings_content| {
6421                            settings_content.terminal.as_ref()?.line_height.as_ref()
6422                        },
6423                        write: |settings_content, value| {
6424                            settings_content
6425                                .terminal
6426                                .get_or_insert_default()
6427                                .line_height = value;
6428                        },
6429                    }
6430                    .unimplemented(),
6431                ),
6432                metadata: None,
6433                files: USER,
6434            }),
6435            SettingsPageItem::SettingItem(SettingItem {
6436                title: "Cursor Shape",
6437                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6438                field: Box::new(SettingField {
6439                    json_path: Some("terminal.cursor_shape"),
6440                    pick: |settings_content| {
6441                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6442                    },
6443                    write: |settings_content, value| {
6444                        settings_content
6445                            .terminal
6446                            .get_or_insert_default()
6447                            .cursor_shape = value;
6448                    },
6449                }),
6450                metadata: None,
6451                files: USER,
6452            }),
6453            SettingsPageItem::SettingItem(SettingItem {
6454                title: "Cursor Blinking",
6455                description: "Sets the cursor blinking behavior in the terminal.",
6456                field: Box::new(SettingField {
6457                    json_path: Some("terminal.blinking"),
6458                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6459                    write: |settings_content, value| {
6460                        settings_content.terminal.get_or_insert_default().blinking = value;
6461                    },
6462                }),
6463                metadata: None,
6464                files: USER,
6465            }),
6466            SettingsPageItem::SettingItem(SettingItem {
6467                title: "Alternate Scroll",
6468                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6469                field: Box::new(SettingField {
6470                    json_path: Some("terminal.alternate_scroll"),
6471                    pick: |settings_content| {
6472                        settings_content
6473                            .terminal
6474                            .as_ref()?
6475                            .alternate_scroll
6476                            .as_ref()
6477                    },
6478                    write: |settings_content, value| {
6479                        settings_content
6480                            .terminal
6481                            .get_or_insert_default()
6482                            .alternate_scroll = value;
6483                    },
6484                }),
6485                metadata: None,
6486                files: USER,
6487            }),
6488            SettingsPageItem::SettingItem(SettingItem {
6489                title: "Minimum Contrast",
6490                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6491                field: Box::new(SettingField {
6492                    json_path: Some("terminal.minimum_contrast"),
6493                    pick: |settings_content| {
6494                        settings_content
6495                            .terminal
6496                            .as_ref()?
6497                            .minimum_contrast
6498                            .as_ref()
6499                    },
6500                    write: |settings_content, value| {
6501                        settings_content
6502                            .terminal
6503                            .get_or_insert_default()
6504                            .minimum_contrast = value;
6505                    },
6506                }),
6507                metadata: None,
6508                files: USER,
6509            }),
6510        ]
6511    }
6512
6513    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6514        [
6515            SettingsPageItem::SectionHeader("Behavior Settings"),
6516            SettingsPageItem::SettingItem(SettingItem {
6517                title: "Option As Meta",
6518                description: "Whether the option key behaves as the meta key.",
6519                field: Box::new(SettingField {
6520                    json_path: Some("terminal.option_as_meta"),
6521                    pick: |settings_content| {
6522                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6523                    },
6524                    write: |settings_content, value| {
6525                        settings_content
6526                            .terminal
6527                            .get_or_insert_default()
6528                            .option_as_meta = value;
6529                    },
6530                }),
6531                metadata: None,
6532                files: USER,
6533            }),
6534            SettingsPageItem::SettingItem(SettingItem {
6535                title: "Copy On Select",
6536                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6537                field: Box::new(SettingField {
6538                    json_path: Some("terminal.copy_on_select"),
6539                    pick: |settings_content| {
6540                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6541                    },
6542                    write: |settings_content, value| {
6543                        settings_content
6544                            .terminal
6545                            .get_or_insert_default()
6546                            .copy_on_select = value;
6547                    },
6548                }),
6549                metadata: None,
6550                files: USER,
6551            }),
6552            SettingsPageItem::SettingItem(SettingItem {
6553                title: "Keep Selection On Copy",
6554                description: "Whether to keep the text selection after copying it to the clipboard.",
6555                field: Box::new(SettingField {
6556                    json_path: Some("terminal.keep_selection_on_copy"),
6557                    pick: |settings_content| {
6558                        settings_content
6559                            .terminal
6560                            .as_ref()?
6561                            .keep_selection_on_copy
6562                            .as_ref()
6563                    },
6564                    write: |settings_content, value| {
6565                        settings_content
6566                            .terminal
6567                            .get_or_insert_default()
6568                            .keep_selection_on_copy = value;
6569                    },
6570                }),
6571                metadata: None,
6572                files: USER,
6573            }),
6574        ]
6575    }
6576
6577    fn layout_settings_section() -> [SettingsPageItem; 3] {
6578        [
6579            SettingsPageItem::SectionHeader("Layout Settings"),
6580            SettingsPageItem::SettingItem(SettingItem {
6581                title: "Default Width",
6582                description: "Default width when the terminal is docked to the left or right (in pixels).",
6583                field: Box::new(SettingField {
6584                    json_path: Some("terminal.default_width"),
6585                    pick: |settings_content| {
6586                        settings_content.terminal.as_ref()?.default_width.as_ref()
6587                    },
6588                    write: |settings_content, value| {
6589                        settings_content
6590                            .terminal
6591                            .get_or_insert_default()
6592                            .default_width = value;
6593                    },
6594                }),
6595                metadata: None,
6596                files: USER,
6597            }),
6598            SettingsPageItem::SettingItem(SettingItem {
6599                title: "Default Height",
6600                description: "Default height when the terminal is docked to the bottom (in pixels).",
6601                field: Box::new(SettingField {
6602                    json_path: Some("terminal.default_height"),
6603                    pick: |settings_content| {
6604                        settings_content.terminal.as_ref()?.default_height.as_ref()
6605                    },
6606                    write: |settings_content, value| {
6607                        settings_content
6608                            .terminal
6609                            .get_or_insert_default()
6610                            .default_height = value;
6611                    },
6612                }),
6613                metadata: None,
6614                files: USER,
6615            }),
6616        ]
6617    }
6618
6619    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6620        [
6621            SettingsPageItem::SectionHeader("Advanced Settings"),
6622            SettingsPageItem::SettingItem(SettingItem {
6623                title: "Max Scroll History Lines",
6624                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6625                field: Box::new(SettingField {
6626                    json_path: Some("terminal.max_scroll_history_lines"),
6627                    pick: |settings_content| {
6628                        settings_content
6629                            .terminal
6630                            .as_ref()?
6631                            .max_scroll_history_lines
6632                            .as_ref()
6633                    },
6634                    write: |settings_content, value| {
6635                        settings_content
6636                            .terminal
6637                            .get_or_insert_default()
6638                            .max_scroll_history_lines = value;
6639                    },
6640                }),
6641                metadata: None,
6642                files: USER,
6643            }),
6644            SettingsPageItem::SettingItem(SettingItem {
6645                title: "Scroll Multiplier",
6646                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6647                field: Box::new(SettingField {
6648                    json_path: Some("terminal.scroll_multiplier"),
6649                    pick: |settings_content| {
6650                        settings_content
6651                            .terminal
6652                            .as_ref()?
6653                            .scroll_multiplier
6654                            .as_ref()
6655                    },
6656                    write: |settings_content, value| {
6657                        settings_content
6658                            .terminal
6659                            .get_or_insert_default()
6660                            .scroll_multiplier = value;
6661                    },
6662                }),
6663                metadata: None,
6664                files: USER,
6665            }),
6666        ]
6667    }
6668
6669    fn toolbar_section() -> [SettingsPageItem; 2] {
6670        [
6671            SettingsPageItem::SectionHeader("Toolbar"),
6672            SettingsPageItem::SettingItem(SettingItem {
6673                title: "Breadcrumbs",
6674                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6675                field: Box::new(SettingField {
6676                    json_path: Some("terminal.toolbar.breadcrumbs"),
6677                    pick: |settings_content| {
6678                        settings_content
6679                            .terminal
6680                            .as_ref()?
6681                            .toolbar
6682                            .as_ref()?
6683                            .breadcrumbs
6684                            .as_ref()
6685                    },
6686                    write: |settings_content, value| {
6687                        settings_content
6688                            .terminal
6689                            .get_or_insert_default()
6690                            .toolbar
6691                            .get_or_insert_default()
6692                            .breadcrumbs = value;
6693                    },
6694                }),
6695                metadata: None,
6696                files: USER,
6697            }),
6698        ]
6699    }
6700
6701    fn scrollbar_section() -> [SettingsPageItem; 2] {
6702        [
6703            SettingsPageItem::SectionHeader("Scrollbar"),
6704            SettingsPageItem::SettingItem(SettingItem {
6705                title: "Show Scrollbar",
6706                description: "When to show the scrollbar in the terminal.",
6707                field: Box::new(SettingField {
6708                    json_path: Some("terminal.scrollbar.show"),
6709                    pick: |settings_content| {
6710                        show_scrollbar_or_editor(settings_content, |settings_content| {
6711                            settings_content
6712                                .terminal
6713                                .as_ref()?
6714                                .scrollbar
6715                                .as_ref()?
6716                                .show
6717                                .as_ref()
6718                        })
6719                    },
6720                    write: |settings_content, value| {
6721                        settings_content
6722                            .terminal
6723                            .get_or_insert_default()
6724                            .scrollbar
6725                            .get_or_insert_default()
6726                            .show = value;
6727                    },
6728                }),
6729                metadata: None,
6730                files: USER,
6731            }),
6732        ]
6733    }
6734
6735    SettingsPage {
6736        title: "Terminal",
6737        items: concat_sections![
6738            environment_section(),
6739            font_section(),
6740            display_settings_section(),
6741            behavior_settings_section(),
6742            layout_settings_section(),
6743            advanced_settings_section(),
6744            toolbar_section(),
6745            scrollbar_section(),
6746        ],
6747    }
6748}
6749
6750fn version_control_page() -> SettingsPage {
6751    fn git_integration_section() -> [SettingsPageItem; 2] {
6752        [
6753            SettingsPageItem::SectionHeader("Git Integration"),
6754            SettingsPageItem::DynamicItem(DynamicItem {
6755                discriminant: SettingItem {
6756                    files: USER,
6757                    title: "Disable Git Integration",
6758                    description: "Disable all Git integration features in Zed.",
6759                    field: Box::new(SettingField::<bool> {
6760                        json_path: Some("git.disable_git"),
6761                        pick: |settings_content| {
6762                            settings_content
6763                                .git
6764                                .as_ref()?
6765                                .enabled
6766                                .as_ref()?
6767                                .disable_git
6768                                .as_ref()
6769                        },
6770                        write: |settings_content, value| {
6771                            settings_content
6772                                .git
6773                                .get_or_insert_default()
6774                                .enabled
6775                                .get_or_insert_default()
6776                                .disable_git = value;
6777                        },
6778                    }),
6779                    metadata: None,
6780                },
6781                pick_discriminant: |settings_content| {
6782                    let disabled = settings_content
6783                        .git
6784                        .as_ref()?
6785                        .enabled
6786                        .as_ref()?
6787                        .disable_git
6788                        .unwrap_or(false);
6789                    Some(if disabled { 0 } else { 1 })
6790                },
6791                fields: vec![
6792                    vec![],
6793                    vec![
6794                        SettingItem {
6795                            files: USER,
6796                            title: "Enable Git Status",
6797                            description: "Show Git status information in the editor.",
6798                            field: Box::new(SettingField::<bool> {
6799                                json_path: Some("git.enable_status"),
6800                                pick: |settings_content| {
6801                                    settings_content
6802                                        .git
6803                                        .as_ref()?
6804                                        .enabled
6805                                        .as_ref()?
6806                                        .enable_status
6807                                        .as_ref()
6808                                },
6809                                write: |settings_content, value| {
6810                                    settings_content
6811                                        .git
6812                                        .get_or_insert_default()
6813                                        .enabled
6814                                        .get_or_insert_default()
6815                                        .enable_status = value;
6816                                },
6817                            }),
6818                            metadata: None,
6819                        },
6820                        SettingItem {
6821                            files: USER,
6822                            title: "Enable Git Diff",
6823                            description: "Show Git diff information in the editor.",
6824                            field: Box::new(SettingField::<bool> {
6825                                json_path: Some("git.enable_diff"),
6826                                pick: |settings_content| {
6827                                    settings_content
6828                                        .git
6829                                        .as_ref()?
6830                                        .enabled
6831                                        .as_ref()?
6832                                        .enable_diff
6833                                        .as_ref()
6834                                },
6835                                write: |settings_content, value| {
6836                                    settings_content
6837                                        .git
6838                                        .get_or_insert_default()
6839                                        .enabled
6840                                        .get_or_insert_default()
6841                                        .enable_diff = value;
6842                                },
6843                            }),
6844                            metadata: None,
6845                        },
6846                    ],
6847                ],
6848            }),
6849        ]
6850    }
6851
6852    fn git_gutter_section() -> [SettingsPageItem; 3] {
6853        [
6854            SettingsPageItem::SectionHeader("Git Gutter"),
6855            SettingsPageItem::SettingItem(SettingItem {
6856                title: "Visibility",
6857                description: "Control whether Git status is shown in the editor's gutter.",
6858                field: Box::new(SettingField {
6859                    json_path: Some("git.git_gutter"),
6860                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6861                    write: |settings_content, value| {
6862                        settings_content.git.get_or_insert_default().git_gutter = value;
6863                    },
6864                }),
6865                metadata: None,
6866                files: USER,
6867            }),
6868            // todo(settings_ui): Figure out the right default for this value in default.json
6869            SettingsPageItem::SettingItem(SettingItem {
6870                title: "Debounce",
6871                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6872                field: Box::new(SettingField {
6873                    json_path: Some("git.gutter_debounce"),
6874                    pick: |settings_content| {
6875                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6876                    },
6877                    write: |settings_content, value| {
6878                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6879                    },
6880                }),
6881                metadata: None,
6882                files: USER,
6883            }),
6884        ]
6885    }
6886
6887    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6888        [
6889            SettingsPageItem::SectionHeader("Inline Git Blame"),
6890            SettingsPageItem::SettingItem(SettingItem {
6891                title: "Enabled",
6892                description: "Whether or not to show Git blame data inline in the currently focused line.",
6893                field: Box::new(SettingField {
6894                    json_path: Some("git.inline_blame.enabled"),
6895                    pick: |settings_content| {
6896                        settings_content
6897                            .git
6898                            .as_ref()?
6899                            .inline_blame
6900                            .as_ref()?
6901                            .enabled
6902                            .as_ref()
6903                    },
6904                    write: |settings_content, value| {
6905                        settings_content
6906                            .git
6907                            .get_or_insert_default()
6908                            .inline_blame
6909                            .get_or_insert_default()
6910                            .enabled = value;
6911                    },
6912                }),
6913                metadata: None,
6914                files: USER,
6915            }),
6916            SettingsPageItem::SettingItem(SettingItem {
6917                title: "Delay",
6918                description: "The delay after which the inline blame information is shown.",
6919                field: Box::new(SettingField {
6920                    json_path: Some("git.inline_blame.delay_ms"),
6921                    pick: |settings_content| {
6922                        settings_content
6923                            .git
6924                            .as_ref()?
6925                            .inline_blame
6926                            .as_ref()?
6927                            .delay_ms
6928                            .as_ref()
6929                    },
6930                    write: |settings_content, value| {
6931                        settings_content
6932                            .git
6933                            .get_or_insert_default()
6934                            .inline_blame
6935                            .get_or_insert_default()
6936                            .delay_ms = value;
6937                    },
6938                }),
6939                metadata: None,
6940                files: USER,
6941            }),
6942            SettingsPageItem::SettingItem(SettingItem {
6943                title: "Padding",
6944                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6945                field: Box::new(SettingField {
6946                    json_path: Some("git.inline_blame.padding"),
6947                    pick: |settings_content| {
6948                        settings_content
6949                            .git
6950                            .as_ref()?
6951                            .inline_blame
6952                            .as_ref()?
6953                            .padding
6954                            .as_ref()
6955                    },
6956                    write: |settings_content, value| {
6957                        settings_content
6958                            .git
6959                            .get_or_insert_default()
6960                            .inline_blame
6961                            .get_or_insert_default()
6962                            .padding = value;
6963                    },
6964                }),
6965                metadata: None,
6966                files: USER,
6967            }),
6968            SettingsPageItem::SettingItem(SettingItem {
6969                title: "Minimum Column",
6970                description: "The minimum column number at which to show the inline blame information.",
6971                field: Box::new(SettingField {
6972                    json_path: Some("git.inline_blame.min_column"),
6973                    pick: |settings_content| {
6974                        settings_content
6975                            .git
6976                            .as_ref()?
6977                            .inline_blame
6978                            .as_ref()?
6979                            .min_column
6980                            .as_ref()
6981                    },
6982                    write: |settings_content, value| {
6983                        settings_content
6984                            .git
6985                            .get_or_insert_default()
6986                            .inline_blame
6987                            .get_or_insert_default()
6988                            .min_column = value;
6989                    },
6990                }),
6991                metadata: None,
6992                files: USER,
6993            }),
6994            SettingsPageItem::SettingItem(SettingItem {
6995                title: "Show Commit Summary",
6996                description: "Show commit summary as part of the inline blame.",
6997                field: Box::new(SettingField {
6998                    json_path: Some("git.inline_blame.show_commit_summary"),
6999                    pick: |settings_content| {
7000                        settings_content
7001                            .git
7002                            .as_ref()?
7003                            .inline_blame
7004                            .as_ref()?
7005                            .show_commit_summary
7006                            .as_ref()
7007                    },
7008                    write: |settings_content, value| {
7009                        settings_content
7010                            .git
7011                            .get_or_insert_default()
7012                            .inline_blame
7013                            .get_or_insert_default()
7014                            .show_commit_summary = value;
7015                    },
7016                }),
7017                metadata: None,
7018                files: USER,
7019            }),
7020        ]
7021    }
7022
7023    fn git_blame_view_section() -> [SettingsPageItem; 2] {
7024        [
7025            SettingsPageItem::SectionHeader("Git Blame View"),
7026            SettingsPageItem::SettingItem(SettingItem {
7027                title: "Show Avatar",
7028                description: "Show the avatar of the author of the commit.",
7029                field: Box::new(SettingField {
7030                    json_path: Some("git.blame.show_avatar"),
7031                    pick: |settings_content| {
7032                        settings_content
7033                            .git
7034                            .as_ref()?
7035                            .blame
7036                            .as_ref()?
7037                            .show_avatar
7038                            .as_ref()
7039                    },
7040                    write: |settings_content, value| {
7041                        settings_content
7042                            .git
7043                            .get_or_insert_default()
7044                            .blame
7045                            .get_or_insert_default()
7046                            .show_avatar = value;
7047                    },
7048                }),
7049                metadata: None,
7050                files: USER,
7051            }),
7052        ]
7053    }
7054
7055    fn branch_picker_section() -> [SettingsPageItem; 2] {
7056        [
7057            SettingsPageItem::SectionHeader("Branch Picker"),
7058            SettingsPageItem::SettingItem(SettingItem {
7059                title: "Show Author Name",
7060                description: "Show author name as part of the commit information in branch picker.",
7061                field: Box::new(SettingField {
7062                    json_path: Some("git.branch_picker.show_author_name"),
7063                    pick: |settings_content| {
7064                        settings_content
7065                            .git
7066                            .as_ref()?
7067                            .branch_picker
7068                            .as_ref()?
7069                            .show_author_name
7070                            .as_ref()
7071                    },
7072                    write: |settings_content, value| {
7073                        settings_content
7074                            .git
7075                            .get_or_insert_default()
7076                            .branch_picker
7077                            .get_or_insert_default()
7078                            .show_author_name = value;
7079                    },
7080                }),
7081                metadata: None,
7082                files: USER,
7083            }),
7084        ]
7085    }
7086
7087    fn git_hunks_section() -> [SettingsPageItem; 3] {
7088        [
7089            SettingsPageItem::SectionHeader("Git Hunks"),
7090            SettingsPageItem::SettingItem(SettingItem {
7091                title: "Hunk Style",
7092                description: "How Git hunks are displayed visually in the editor.",
7093                field: Box::new(SettingField {
7094                    json_path: Some("git.hunk_style"),
7095                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
7096                    write: |settings_content, value| {
7097                        settings_content.git.get_or_insert_default().hunk_style = value;
7098                    },
7099                }),
7100                metadata: None,
7101                files: USER,
7102            }),
7103            SettingsPageItem::SettingItem(SettingItem {
7104                title: "Path Style",
7105                description: "Should the name or path be displayed first in the git view.",
7106                field: Box::new(SettingField {
7107                    json_path: Some("git.path_style"),
7108                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
7109                    write: |settings_content, value| {
7110                        settings_content.git.get_or_insert_default().path_style = value;
7111                    },
7112                }),
7113                metadata: None,
7114                files: USER,
7115            }),
7116        ]
7117    }
7118
7119    SettingsPage {
7120        title: "Version Control",
7121        items: concat_sections![
7122            git_integration_section(),
7123            git_gutter_section(),
7124            inline_git_blame_section(),
7125            git_blame_view_section(),
7126            branch_picker_section(),
7127            git_hunks_section(),
7128        ],
7129    }
7130}
7131
7132fn collaboration_page() -> SettingsPage {
7133    fn calls_section() -> [SettingsPageItem; 3] {
7134        [
7135            SettingsPageItem::SectionHeader("Calls"),
7136            SettingsPageItem::SettingItem(SettingItem {
7137                title: "Mute On Join",
7138                description: "Whether the microphone should be muted when joining a channel or a call.",
7139                field: Box::new(SettingField {
7140                    json_path: Some("calls.mute_on_join"),
7141                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
7142                    write: |settings_content, value| {
7143                        settings_content.calls.get_or_insert_default().mute_on_join = value;
7144                    },
7145                }),
7146                metadata: None,
7147                files: USER,
7148            }),
7149            SettingsPageItem::SettingItem(SettingItem {
7150                title: "Share On Join",
7151                description: "Whether your current project should be shared when joining an empty channel.",
7152                field: Box::new(SettingField {
7153                    json_path: Some("calls.share_on_join"),
7154                    pick: |settings_content| {
7155                        settings_content.calls.as_ref()?.share_on_join.as_ref()
7156                    },
7157                    write: |settings_content, value| {
7158                        settings_content.calls.get_or_insert_default().share_on_join = value;
7159                    },
7160                }),
7161                metadata: None,
7162                files: USER,
7163            }),
7164        ]
7165    }
7166
7167    fn audio_settings() -> [SettingsPageItem; 3] {
7168        [
7169            SettingsPageItem::ActionLink(ActionLink {
7170                title: "Test Audio".into(),
7171                description: Some("Test your microphone and speaker setup".into()),
7172                button_text: "Test Audio".into(),
7173                on_click: Arc::new(|_settings_window, window, cx| {
7174                    open_audio_test_window(window, cx);
7175                }),
7176                files: USER,
7177            }),
7178            SettingsPageItem::SettingItem(SettingItem {
7179                title: "Output Audio Device",
7180                description: "Select output audio device",
7181                field: Box::new(SettingField {
7182                    json_path: Some("audio.experimental.output_audio_device"),
7183                    pick: |settings_content| {
7184                        settings_content
7185                            .audio
7186                            .as_ref()?
7187                            .output_audio_device
7188                            .as_ref()
7189                            .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
7190                    },
7191                    write: |settings_content, value| {
7192                        settings_content
7193                            .audio
7194                            .get_or_insert_default()
7195                            .output_audio_device = value;
7196                    },
7197                }),
7198                metadata: None,
7199                files: USER,
7200            }),
7201            SettingsPageItem::SettingItem(SettingItem {
7202                title: "Input Audio Device",
7203                description: "Select input audio device",
7204                field: Box::new(SettingField {
7205                    json_path: Some("audio.experimental.input_audio_device"),
7206                    pick: |settings_content| {
7207                        settings_content
7208                            .audio
7209                            .as_ref()?
7210                            .input_audio_device
7211                            .as_ref()
7212                            .or(DEFAULT_EMPTY_AUDIO_INPUT)
7213                    },
7214                    write: |settings_content, value| {
7215                        settings_content
7216                            .audio
7217                            .get_or_insert_default()
7218                            .input_audio_device = value;
7219                    },
7220                }),
7221                metadata: None,
7222                files: USER,
7223            }),
7224        ]
7225    }
7226
7227    SettingsPage {
7228        title: "Collaboration",
7229        items: concat_sections![calls_section(), audio_settings()],
7230    }
7231}
7232
7233fn ai_page(cx: &App) -> SettingsPage {
7234    fn general_section() -> [SettingsPageItem; 2] {
7235        [
7236            SettingsPageItem::SectionHeader("General"),
7237            SettingsPageItem::SettingItem(SettingItem {
7238                title: "Disable AI",
7239                description: "Whether to disable all AI features in Zed.",
7240                field: Box::new(SettingField {
7241                    json_path: Some("disable_ai"),
7242                    pick: |settings_content| settings_content.project.disable_ai.as_ref(),
7243                    write: |settings_content, value| {
7244                        settings_content.project.disable_ai = value;
7245                    },
7246                }),
7247                metadata: None,
7248                files: USER | PROJECT,
7249            }),
7250        ]
7251    }
7252
7253    fn agent_configuration_section(cx: &App) -> Box<[SettingsPageItem]> {
7254        let mut items = vec![
7255            SettingsPageItem::SectionHeader("Agent Configuration"),
7256            SettingsPageItem::SubPageLink(SubPageLink {
7257                title: "Tool Permissions".into(),
7258                r#type: Default::default(),
7259                json_path: Some("agent.tool_permissions"),
7260                description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
7261                in_json: true,
7262                files: USER,
7263                render: render_tool_permissions_setup_page,
7264            }),
7265        ];
7266
7267        if cx.has_flag::<AgentV2FeatureFlag>() {
7268            items.push(SettingsPageItem::SettingItem(SettingItem {
7269                title: "New Thread Location",
7270                description: "Whether to start a new thread in the current local project or in a new Git worktree.",
7271                field: Box::new(SettingField {
7272                    json_path: Some("agent.new_thread_location"),
7273                    pick: |settings_content| {
7274                        settings_content
7275                            .agent
7276                            .as_ref()?
7277                            .new_thread_location
7278                            .as_ref()
7279                    },
7280                    write: |settings_content, value| {
7281                        settings_content
7282                            .agent
7283                            .get_or_insert_default()
7284                            .new_thread_location = value;
7285                    },
7286                }),
7287                metadata: None,
7288                files: USER,
7289            }));
7290        }
7291
7292        items.extend([
7293            SettingsPageItem::SettingItem(SettingItem {
7294                title: "Single File Review",
7295                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
7296                field: Box::new(SettingField {
7297                    json_path: Some("agent.single_file_review"),
7298                    pick: |settings_content| {
7299                        settings_content.agent.as_ref()?.single_file_review.as_ref()
7300                    },
7301                    write: |settings_content, value| {
7302                        settings_content
7303                            .agent
7304                            .get_or_insert_default()
7305                            .single_file_review = value;
7306                    },
7307                }),
7308                metadata: None,
7309                files: USER,
7310            }),
7311            SettingsPageItem::SettingItem(SettingItem {
7312                title: "Enable Feedback",
7313                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
7314                field: Box::new(SettingField {
7315                    json_path: Some("agent.enable_feedback"),
7316                    pick: |settings_content| {
7317                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
7318                    },
7319                    write: |settings_content, value| {
7320                        settings_content
7321                            .agent
7322                            .get_or_insert_default()
7323                            .enable_feedback = value;
7324                    },
7325                }),
7326                metadata: None,
7327                files: USER,
7328            }),
7329            SettingsPageItem::SettingItem(SettingItem {
7330                title: "Notify When Agent Waiting",
7331                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7332                field: Box::new(SettingField {
7333                    json_path: Some("agent.notify_when_agent_waiting"),
7334                    pick: |settings_content| {
7335                        settings_content
7336                            .agent
7337                            .as_ref()?
7338                            .notify_when_agent_waiting
7339                            .as_ref()
7340                    },
7341                    write: |settings_content, value| {
7342                        settings_content
7343                            .agent
7344                            .get_or_insert_default()
7345                            .notify_when_agent_waiting = value;
7346                    },
7347                }),
7348                metadata: None,
7349                files: USER,
7350            }),
7351            SettingsPageItem::SettingItem(SettingItem {
7352                title: "Play Sound When Agent Done",
7353                description: "When to play a sound when the agent has either completed its response, or needs user input.",
7354                field: Box::new(SettingField {
7355                    json_path: Some("agent.play_sound_when_agent_done"),
7356                    pick: |settings_content| {
7357                        settings_content
7358                            .agent
7359                            .as_ref()?
7360                            .play_sound_when_agent_done
7361                            .as_ref()
7362                    },
7363                    write: |settings_content, value| {
7364                        settings_content
7365                            .agent
7366                            .get_or_insert_default()
7367                            .play_sound_when_agent_done = value;
7368                    },
7369                }),
7370                metadata: None,
7371                files: USER,
7372            }),
7373            SettingsPageItem::SettingItem(SettingItem {
7374                title: "Expand Edit Card",
7375                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7376                field: Box::new(SettingField {
7377                    json_path: Some("agent.expand_edit_card"),
7378                    pick: |settings_content| {
7379                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7380                    },
7381                    write: |settings_content, value| {
7382                        settings_content
7383                            .agent
7384                            .get_or_insert_default()
7385                            .expand_edit_card = value;
7386                    },
7387                }),
7388                metadata: None,
7389                files: USER,
7390            }),
7391            SettingsPageItem::SettingItem(SettingItem {
7392                title: "Expand Terminal Card",
7393                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7394                field: Box::new(SettingField {
7395                    json_path: Some("agent.expand_terminal_card"),
7396                    pick: |settings_content| {
7397                        settings_content
7398                            .agent
7399                            .as_ref()?
7400                            .expand_terminal_card
7401                            .as_ref()
7402                    },
7403                    write: |settings_content, value| {
7404                        settings_content
7405                            .agent
7406                            .get_or_insert_default()
7407                            .expand_terminal_card = value;
7408                    },
7409                }),
7410                metadata: None,
7411                files: USER,
7412            }),
7413            SettingsPageItem::SettingItem(SettingItem {
7414                title: "Thinking Display",
7415                description: "How thinking blocks should be displayed by default. 'Auto' fully expands during streaming, then auto-collapses when done. 'Preview' auto-expands with a height constraint during streaming. 'Always Expanded' shows full content. 'Always Collapsed' keeps them collapsed.",
7416                field: Box::new(SettingField {
7417                    json_path: Some("agent.thinking_display"),
7418                    pick: |settings_content| {
7419                        settings_content
7420                            .agent
7421                            .as_ref()?
7422                            .thinking_display
7423                            .as_ref()
7424                    },
7425                    write: |settings_content, value| {
7426                        settings_content
7427                            .agent
7428                            .get_or_insert_default()
7429                            .thinking_display = value;
7430                    },
7431                }),
7432                metadata: None,
7433                files: USER,
7434            }),
7435            SettingsPageItem::SettingItem(SettingItem {
7436                title: "Cancel Generation On Terminal Stop",
7437                description: "Whether clicking the stop button on a running terminal tool should also cancel the agent's generation. Note that this only applies to the stop button, not to ctrl+c inside the terminal.",
7438                field: Box::new(SettingField {
7439                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
7440                    pick: |settings_content| {
7441                        settings_content
7442                            .agent
7443                            .as_ref()?
7444                            .cancel_generation_on_terminal_stop
7445                            .as_ref()
7446                    },
7447                    write: |settings_content, value| {
7448                        settings_content
7449                            .agent
7450                            .get_or_insert_default()
7451                            .cancel_generation_on_terminal_stop = value;
7452                    },
7453                }),
7454                metadata: None,
7455                files: USER,
7456            }),
7457            SettingsPageItem::SettingItem(SettingItem {
7458                title: "Use Modifier To Send",
7459                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7460                field: Box::new(SettingField {
7461                    json_path: Some("agent.use_modifier_to_send"),
7462                    pick: |settings_content| {
7463                        settings_content
7464                            .agent
7465                            .as_ref()?
7466                            .use_modifier_to_send
7467                            .as_ref()
7468                    },
7469                    write: |settings_content, value| {
7470                        settings_content
7471                            .agent
7472                            .get_or_insert_default()
7473                            .use_modifier_to_send = value;
7474                    },
7475                }),
7476                metadata: None,
7477                files: USER,
7478            }),
7479            SettingsPageItem::SettingItem(SettingItem {
7480                title: "Message Editor Min Lines",
7481                description: "Minimum number of lines to display in the agent message editor.",
7482                field: Box::new(SettingField {
7483                    json_path: Some("agent.message_editor_min_lines"),
7484                    pick: |settings_content| {
7485                        settings_content
7486                            .agent
7487                            .as_ref()?
7488                            .message_editor_min_lines
7489                            .as_ref()
7490                    },
7491                    write: |settings_content, value| {
7492                        settings_content
7493                            .agent
7494                            .get_or_insert_default()
7495                            .message_editor_min_lines = value;
7496                    },
7497                }),
7498                metadata: None,
7499                files: USER,
7500            }),
7501            SettingsPageItem::SettingItem(SettingItem {
7502                title: "Show Turn Stats",
7503                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7504                field: Box::new(SettingField {
7505                    json_path: Some("agent.show_turn_stats"),
7506                    pick: |settings_content| {
7507                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7508                    },
7509                    write: |settings_content, value| {
7510                        settings_content
7511                            .agent
7512                            .get_or_insert_default()
7513                            .show_turn_stats = value;
7514                    },
7515                }),
7516                metadata: None,
7517                files: USER,
7518            }),
7519        ]);
7520
7521        items.into_boxed_slice()
7522    }
7523
7524    fn context_servers_section() -> [SettingsPageItem; 2] {
7525        [
7526            SettingsPageItem::SectionHeader("Context Servers"),
7527            SettingsPageItem::SettingItem(SettingItem {
7528                title: "Context Server Timeout",
7529                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7530                field: Box::new(SettingField {
7531                    json_path: Some("context_server_timeout"),
7532                    pick: |settings_content| {
7533                        settings_content.project.context_server_timeout.as_ref()
7534                    },
7535                    write: |settings_content, value| {
7536                        settings_content.project.context_server_timeout = value;
7537                    },
7538                }),
7539                metadata: None,
7540                files: USER | PROJECT,
7541            }),
7542        ]
7543    }
7544
7545    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 1] {
7546        [SettingsPageItem::SettingItem(SettingItem {
7547            title: "Display Mode",
7548            description: "When to show edit predictions previews in buffer. The eager mode displays them inline, while the subtle mode displays them only when holding a modifier key.",
7549            field: Box::new(SettingField {
7550                json_path: Some("edit_prediction.display_mode"),
7551                pick: |settings_content| {
7552                    settings_content
7553                        .project
7554                        .all_languages
7555                        .edit_predictions
7556                        .as_ref()?
7557                        .mode
7558                        .as_ref()
7559                },
7560                write: |settings_content, value| {
7561                    settings_content
7562                        .project
7563                        .all_languages
7564                        .edit_predictions
7565                        .get_or_insert_default()
7566                        .mode = value;
7567                },
7568            }),
7569            metadata: None,
7570            files: USER,
7571        })]
7572    }
7573
7574    SettingsPage {
7575        title: "AI",
7576        items: concat_sections![
7577            general_section(),
7578            agent_configuration_section(cx),
7579            context_servers_section(),
7580            edit_prediction_language_settings_section(),
7581            edit_prediction_display_sub_section()
7582        ],
7583    }
7584}
7585
7586fn network_page() -> SettingsPage {
7587    fn network_section() -> [SettingsPageItem; 3] {
7588        [
7589            SettingsPageItem::SectionHeader("Network"),
7590            SettingsPageItem::SettingItem(SettingItem {
7591                title: "Proxy",
7592                description: "The proxy to use for network requests.",
7593                field: Box::new(SettingField {
7594                    json_path: Some("proxy"),
7595                    pick: |settings_content| settings_content.proxy.as_ref(),
7596                    write: |settings_content, value| {
7597                        settings_content.proxy = value;
7598                    },
7599                }),
7600                metadata: Some(Box::new(SettingsFieldMetadata {
7601                    placeholder: Some("socks5h://localhost:10808"),
7602                    ..Default::default()
7603                })),
7604                files: USER,
7605            }),
7606            SettingsPageItem::SettingItem(SettingItem {
7607                title: "Server URL",
7608                description: "The URL of the Zed server to connect to.",
7609                field: Box::new(SettingField {
7610                    json_path: Some("server_url"),
7611                    pick: |settings_content| settings_content.server_url.as_ref(),
7612                    write: |settings_content, value| {
7613                        settings_content.server_url = value;
7614                    },
7615                }),
7616                metadata: Some(Box::new(SettingsFieldMetadata {
7617                    placeholder: Some("https://zed.dev"),
7618                    ..Default::default()
7619                })),
7620                files: USER,
7621            }),
7622        ]
7623    }
7624
7625    SettingsPage {
7626        title: "Network",
7627        items: concat_sections![network_section()],
7628    }
7629}
7630
7631fn language_settings_field<T>(
7632    settings_content: &SettingsContent,
7633    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7634) -> Option<&T> {
7635    let all_languages = &settings_content.project.all_languages;
7636
7637    active_language()
7638        .and_then(|current_language_name| {
7639            all_languages
7640                .languages
7641                .0
7642                .get(current_language_name.as_ref())
7643        })
7644        .and_then(get_language_setting_field)
7645        .or_else(|| get_language_setting_field(&all_languages.defaults))
7646}
7647
7648fn language_settings_field_mut<T>(
7649    settings_content: &mut SettingsContent,
7650    value: Option<T>,
7651    write: fn(&mut LanguageSettingsContent, Option<T>),
7652) {
7653    let all_languages = &mut settings_content.project.all_languages;
7654    let language_content = if let Some(current_language) = active_language() {
7655        all_languages
7656            .languages
7657            .0
7658            .entry(current_language.to_string())
7659            .or_default()
7660    } else {
7661        &mut all_languages.defaults
7662    };
7663    write(language_content, value);
7664}
7665
7666fn language_settings_data() -> Box<[SettingsPageItem]> {
7667    fn indentation_section() -> [SettingsPageItem; 5] {
7668        [
7669            SettingsPageItem::SectionHeader("Indentation"),
7670            SettingsPageItem::SettingItem(SettingItem {
7671                title: "Tab Size",
7672                description: "How many columns a tab should occupy.",
7673                field: Box::new(SettingField {
7674                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7675                    pick: |settings_content| {
7676                        language_settings_field(settings_content, |language| {
7677                            language.tab_size.as_ref()
7678                        })
7679                    },
7680                    write: |settings_content, value| {
7681                        language_settings_field_mut(settings_content, value, |language, value| {
7682                            language.tab_size = value;
7683                        })
7684                    },
7685                }),
7686                metadata: None,
7687                files: USER | PROJECT,
7688            }),
7689            SettingsPageItem::SettingItem(SettingItem {
7690                title: "Hard Tabs",
7691                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7692                field: Box::new(SettingField {
7693                    json_path: Some("languages.$(language).hard_tabs"),
7694                    pick: |settings_content| {
7695                        language_settings_field(settings_content, |language| {
7696                            language.hard_tabs.as_ref()
7697                        })
7698                    },
7699                    write: |settings_content, value| {
7700                        language_settings_field_mut(settings_content, value, |language, value| {
7701                            language.hard_tabs = value;
7702                        })
7703                    },
7704                }),
7705                metadata: None,
7706                files: USER | PROJECT,
7707            }),
7708            SettingsPageItem::SettingItem(SettingItem {
7709                title: "Auto Indent",
7710                description: "Controls automatic indentation behavior when typing.",
7711                field: Box::new(SettingField {
7712                    json_path: Some("languages.$(language).auto_indent"),
7713                    pick: |settings_content| {
7714                        language_settings_field(settings_content, |language| {
7715                            language.auto_indent.as_ref()
7716                        })
7717                    },
7718                    write: |settings_content, value| {
7719                        language_settings_field_mut(settings_content, value, |language, value| {
7720                            language.auto_indent = value;
7721                        })
7722                    },
7723                }),
7724                metadata: None,
7725                files: USER | PROJECT,
7726            }),
7727            SettingsPageItem::SettingItem(SettingItem {
7728                title: "Auto Indent On Paste",
7729                description: "Whether indentation of pasted content should be adjusted based on the context.",
7730                field: Box::new(SettingField {
7731                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7732                    pick: |settings_content| {
7733                        language_settings_field(settings_content, |language| {
7734                            language.auto_indent_on_paste.as_ref()
7735                        })
7736                    },
7737                    write: |settings_content, value| {
7738                        language_settings_field_mut(settings_content, value, |language, value| {
7739                            language.auto_indent_on_paste = value;
7740                        })
7741                    },
7742                }),
7743                metadata: None,
7744                files: USER | PROJECT,
7745            }),
7746        ]
7747    }
7748
7749    fn wrapping_section() -> [SettingsPageItem; 6] {
7750        [
7751            SettingsPageItem::SectionHeader("Wrapping"),
7752            SettingsPageItem::SettingItem(SettingItem {
7753                title: "Soft Wrap",
7754                description: "How to soft-wrap long lines of text.",
7755                field: Box::new(SettingField {
7756                    json_path: Some("languages.$(language).soft_wrap"),
7757                    pick: |settings_content| {
7758                        language_settings_field(settings_content, |language| {
7759                            language.soft_wrap.as_ref()
7760                        })
7761                    },
7762                    write: |settings_content, value| {
7763                        language_settings_field_mut(settings_content, value, |language, value| {
7764                            language.soft_wrap = value;
7765                        })
7766                    },
7767                }),
7768                metadata: None,
7769                files: USER | PROJECT,
7770            }),
7771            SettingsPageItem::SettingItem(SettingItem {
7772                title: "Show Wrap Guides",
7773                description: "Show wrap guides in the editor.",
7774                field: Box::new(SettingField {
7775                    json_path: Some("languages.$(language).show_wrap_guides"),
7776                    pick: |settings_content| {
7777                        language_settings_field(settings_content, |language| {
7778                            language.show_wrap_guides.as_ref()
7779                        })
7780                    },
7781                    write: |settings_content, value| {
7782                        language_settings_field_mut(settings_content, value, |language, value| {
7783                            language.show_wrap_guides = value;
7784                        })
7785                    },
7786                }),
7787                metadata: None,
7788                files: USER | PROJECT,
7789            }),
7790            SettingsPageItem::SettingItem(SettingItem {
7791                title: "Preferred Line Length",
7792                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7793                field: Box::new(SettingField {
7794                    json_path: Some("languages.$(language).preferred_line_length"),
7795                    pick: |settings_content| {
7796                        language_settings_field(settings_content, |language| {
7797                            language.preferred_line_length.as_ref()
7798                        })
7799                    },
7800                    write: |settings_content, value| {
7801                        language_settings_field_mut(settings_content, value, |language, value| {
7802                            language.preferred_line_length = value;
7803                        })
7804                    },
7805                }),
7806                metadata: None,
7807                files: USER | PROJECT,
7808            }),
7809            SettingsPageItem::SettingItem(SettingItem {
7810                title: "Wrap Guides",
7811                description: "Character counts at which to show wrap guides in the editor.",
7812                field: Box::new(
7813                    SettingField {
7814                        json_path: Some("languages.$(language).wrap_guides"),
7815                        pick: |settings_content| {
7816                            language_settings_field(settings_content, |language| {
7817                                language.wrap_guides.as_ref()
7818                            })
7819                        },
7820                        write: |settings_content, value| {
7821                            language_settings_field_mut(
7822                                settings_content,
7823                                value,
7824                                |language, value| {
7825                                    language.wrap_guides = value;
7826                                },
7827                            )
7828                        },
7829                    }
7830                    .unimplemented(),
7831                ),
7832                metadata: None,
7833                files: USER | PROJECT,
7834            }),
7835            SettingsPageItem::SettingItem(SettingItem {
7836                title: "Allow Rewrap",
7837                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7838                field: Box::new(SettingField {
7839                    json_path: Some("languages.$(language).allow_rewrap"),
7840                    pick: |settings_content| {
7841                        language_settings_field(settings_content, |language| {
7842                            language.allow_rewrap.as_ref()
7843                        })
7844                    },
7845                    write: |settings_content, value| {
7846                        language_settings_field_mut(settings_content, value, |language, value| {
7847                            language.allow_rewrap = value;
7848                        })
7849                    },
7850                }),
7851                metadata: None,
7852                files: USER | PROJECT,
7853            }),
7854        ]
7855    }
7856
7857    fn indent_guides_section() -> [SettingsPageItem; 6] {
7858        [
7859            SettingsPageItem::SectionHeader("Indent Guides"),
7860            SettingsPageItem::SettingItem(SettingItem {
7861                title: "Enabled",
7862                description: "Display indent guides in the editor.",
7863                field: Box::new(SettingField {
7864                    json_path: Some("languages.$(language).indent_guides.enabled"),
7865                    pick: |settings_content| {
7866                        language_settings_field(settings_content, |language| {
7867                            language
7868                                .indent_guides
7869                                .as_ref()
7870                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7871                        })
7872                    },
7873                    write: |settings_content, value| {
7874                        language_settings_field_mut(settings_content, value, |language, value| {
7875                            language.indent_guides.get_or_insert_default().enabled = value;
7876                        })
7877                    },
7878                }),
7879                metadata: None,
7880                files: USER | PROJECT,
7881            }),
7882            SettingsPageItem::SettingItem(SettingItem {
7883                title: "Line Width",
7884                description: "The width of the indent guides in pixels, between 1 and 10.",
7885                field: Box::new(SettingField {
7886                    json_path: Some("languages.$(language).indent_guides.line_width"),
7887                    pick: |settings_content| {
7888                        language_settings_field(settings_content, |language| {
7889                            language
7890                                .indent_guides
7891                                .as_ref()
7892                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7893                        })
7894                    },
7895                    write: |settings_content, value| {
7896                        language_settings_field_mut(settings_content, value, |language, value| {
7897                            language.indent_guides.get_or_insert_default().line_width = value;
7898                        })
7899                    },
7900                }),
7901                metadata: None,
7902                files: USER | PROJECT,
7903            }),
7904            SettingsPageItem::SettingItem(SettingItem {
7905                title: "Active Line Width",
7906                description: "The width of the active indent guide in pixels, between 1 and 10.",
7907                field: Box::new(SettingField {
7908                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7909                    pick: |settings_content| {
7910                        language_settings_field(settings_content, |language| {
7911                            language
7912                                .indent_guides
7913                                .as_ref()
7914                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7915                        })
7916                    },
7917                    write: |settings_content, value| {
7918                        language_settings_field_mut(settings_content, value, |language, value| {
7919                            language
7920                                .indent_guides
7921                                .get_or_insert_default()
7922                                .active_line_width = value;
7923                        })
7924                    },
7925                }),
7926                metadata: None,
7927                files: USER | PROJECT,
7928            }),
7929            SettingsPageItem::SettingItem(SettingItem {
7930                title: "Coloring",
7931                description: "Determines how indent guides are colored.",
7932                field: Box::new(SettingField {
7933                    json_path: Some("languages.$(language).indent_guides.coloring"),
7934                    pick: |settings_content| {
7935                        language_settings_field(settings_content, |language| {
7936                            language
7937                                .indent_guides
7938                                .as_ref()
7939                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7940                        })
7941                    },
7942                    write: |settings_content, value| {
7943                        language_settings_field_mut(settings_content, value, |language, value| {
7944                            language.indent_guides.get_or_insert_default().coloring = value;
7945                        })
7946                    },
7947                }),
7948                metadata: None,
7949                files: USER | PROJECT,
7950            }),
7951            SettingsPageItem::SettingItem(SettingItem {
7952                title: "Background Coloring",
7953                description: "Determines how indent guide backgrounds are colored.",
7954                field: Box::new(SettingField {
7955                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7956                    pick: |settings_content| {
7957                        language_settings_field(settings_content, |language| {
7958                            language.indent_guides.as_ref().and_then(|indent_guides| {
7959                                indent_guides.background_coloring.as_ref()
7960                            })
7961                        })
7962                    },
7963                    write: |settings_content, value| {
7964                        language_settings_field_mut(settings_content, value, |language, value| {
7965                            language
7966                                .indent_guides
7967                                .get_or_insert_default()
7968                                .background_coloring = value;
7969                        })
7970                    },
7971                }),
7972                metadata: None,
7973                files: USER | PROJECT,
7974            }),
7975        ]
7976    }
7977
7978    fn formatting_section() -> [SettingsPageItem; 7] {
7979        [
7980            SettingsPageItem::SectionHeader("Formatting"),
7981            SettingsPageItem::SettingItem(SettingItem {
7982                title: "Format On Save",
7983                description: "Whether or not to perform a buffer format before saving.",
7984                field: Box::new(
7985                    // TODO(settings_ui): this setting should just be a bool
7986                    SettingField {
7987                        json_path: Some("languages.$(language).format_on_save"),
7988                        pick: |settings_content| {
7989                            language_settings_field(settings_content, |language| {
7990                                language.format_on_save.as_ref()
7991                            })
7992                        },
7993                        write: |settings_content, value| {
7994                            language_settings_field_mut(
7995                                settings_content,
7996                                value,
7997                                |language, value| {
7998                                    language.format_on_save = value;
7999                                },
8000                            )
8001                        },
8002                    },
8003                ),
8004                metadata: None,
8005                files: USER | PROJECT,
8006            }),
8007            SettingsPageItem::SettingItem(SettingItem {
8008                title: "Remove Trailing Whitespace On Save",
8009                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
8010                field: Box::new(SettingField {
8011                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
8012                    pick: |settings_content| {
8013                        language_settings_field(settings_content, |language| {
8014                            language.remove_trailing_whitespace_on_save.as_ref()
8015                        })
8016                    },
8017                    write: |settings_content, value| {
8018                        language_settings_field_mut(settings_content, value, |language, value| {
8019                            language.remove_trailing_whitespace_on_save = value;
8020                        })
8021                    },
8022                }),
8023                metadata: None,
8024                files: USER | PROJECT,
8025            }),
8026            SettingsPageItem::SettingItem(SettingItem {
8027                title: "Ensure Final Newline On Save",
8028                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
8029                field: Box::new(SettingField {
8030                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
8031                    pick: |settings_content| {
8032                        language_settings_field(settings_content, |language| {
8033                            language.ensure_final_newline_on_save.as_ref()
8034                        })
8035                    },
8036                    write: |settings_content, value| {
8037                        language_settings_field_mut(settings_content, value, |language, value| {
8038                            language.ensure_final_newline_on_save = value;
8039                        })
8040                    },
8041                }),
8042                metadata: None,
8043                files: USER | PROJECT,
8044            }),
8045            SettingsPageItem::SettingItem(SettingItem {
8046                title: "Formatter",
8047                description: "How to perform a buffer format.",
8048                field: Box::new(
8049                    SettingField {
8050                        json_path: Some("languages.$(language).formatter"),
8051                        pick: |settings_content| {
8052                            language_settings_field(settings_content, |language| {
8053                                language.formatter.as_ref()
8054                            })
8055                        },
8056                        write: |settings_content, value| {
8057                            language_settings_field_mut(
8058                                settings_content,
8059                                value,
8060                                |language, value| {
8061                                    language.formatter = value;
8062                                },
8063                            )
8064                        },
8065                    }
8066                    .unimplemented(),
8067                ),
8068                metadata: None,
8069                files: USER | PROJECT,
8070            }),
8071            SettingsPageItem::SettingItem(SettingItem {
8072                title: "Use On Type Format",
8073                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
8074                field: Box::new(SettingField {
8075                    json_path: Some("languages.$(language).use_on_type_format"),
8076                    pick: |settings_content| {
8077                        language_settings_field(settings_content, |language| {
8078                            language.use_on_type_format.as_ref()
8079                        })
8080                    },
8081                    write: |settings_content, value| {
8082                        language_settings_field_mut(settings_content, value, |language, value| {
8083                            language.use_on_type_format = value;
8084                        })
8085                    },
8086                }),
8087                metadata: None,
8088                files: USER | PROJECT,
8089            }),
8090            SettingsPageItem::SettingItem(SettingItem {
8091                title: "Code Actions On Format",
8092                description: "Additional code actions to run when formatting.",
8093                field: Box::new(
8094                    SettingField {
8095                        json_path: Some("languages.$(language).code_actions_on_format"),
8096                        pick: |settings_content| {
8097                            language_settings_field(settings_content, |language| {
8098                                language.code_actions_on_format.as_ref()
8099                            })
8100                        },
8101                        write: |settings_content, value| {
8102                            language_settings_field_mut(
8103                                settings_content,
8104                                value,
8105                                |language, value| {
8106                                    language.code_actions_on_format = value;
8107                                },
8108                            )
8109                        },
8110                    }
8111                    .unimplemented(),
8112                ),
8113                metadata: None,
8114                files: USER | PROJECT,
8115            }),
8116        ]
8117    }
8118
8119    fn autoclose_section() -> [SettingsPageItem; 5] {
8120        [
8121            SettingsPageItem::SectionHeader("Autoclose"),
8122            SettingsPageItem::SettingItem(SettingItem {
8123                title: "Use Autoclose",
8124                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
8125                field: Box::new(SettingField {
8126                    json_path: Some("languages.$(language).use_autoclose"),
8127                    pick: |settings_content| {
8128                        language_settings_field(settings_content, |language| {
8129                            language.use_autoclose.as_ref()
8130                        })
8131                    },
8132                    write: |settings_content, value| {
8133                        language_settings_field_mut(settings_content, value, |language, value| {
8134                            language.use_autoclose = value;
8135                        })
8136                    },
8137                }),
8138                metadata: None,
8139                files: USER | PROJECT,
8140            }),
8141            SettingsPageItem::SettingItem(SettingItem {
8142                title: "Use Auto Surround",
8143                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
8144                field: Box::new(SettingField {
8145                    json_path: Some("languages.$(language).use_auto_surround"),
8146                    pick: |settings_content| {
8147                        language_settings_field(settings_content, |language| {
8148                            language.use_auto_surround.as_ref()
8149                        })
8150                    },
8151                    write: |settings_content, value| {
8152                        language_settings_field_mut(settings_content, value, |language, value| {
8153                            language.use_auto_surround = value;
8154                        })
8155                    },
8156                }),
8157                metadata: None,
8158                files: USER | PROJECT,
8159            }),
8160            SettingsPageItem::SettingItem(SettingItem {
8161                title: "Always Treat Brackets As Autoclosed",
8162                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
8163                field: Box::new(SettingField {
8164                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
8165                    pick: |settings_content| {
8166                        language_settings_field(settings_content, |language| {
8167                            language.always_treat_brackets_as_autoclosed.as_ref()
8168                        })
8169                    },
8170                    write: |settings_content, value| {
8171                        language_settings_field_mut(settings_content, value, |language, value| {
8172                            language.always_treat_brackets_as_autoclosed = value;
8173                        })
8174                    },
8175                }),
8176                metadata: None,
8177                files: USER | PROJECT,
8178            }),
8179            SettingsPageItem::SettingItem(SettingItem {
8180                title: "JSX Tag Auto Close",
8181                description: "Whether to automatically close JSX tags.",
8182                field: Box::new(SettingField {
8183                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
8184                    // TODO(settings_ui): this setting should just be a bool
8185                    pick: |settings_content| {
8186                        language_settings_field(settings_content, |language| {
8187                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
8188                        })
8189                    },
8190                    write: |settings_content, value| {
8191                        language_settings_field_mut(settings_content, value, |language, value| {
8192                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
8193                        })
8194                    },
8195                }),
8196                metadata: None,
8197                files: USER | PROJECT,
8198            }),
8199        ]
8200    }
8201
8202    fn whitespace_section() -> [SettingsPageItem; 4] {
8203        [
8204            SettingsPageItem::SectionHeader("Whitespace"),
8205            SettingsPageItem::SettingItem(SettingItem {
8206                title: "Show Whitespaces",
8207                description: "Whether to show tabs and spaces in the editor.",
8208                field: Box::new(SettingField {
8209                    json_path: Some("languages.$(language).show_whitespaces"),
8210                    pick: |settings_content| {
8211                        language_settings_field(settings_content, |language| {
8212                            language.show_whitespaces.as_ref()
8213                        })
8214                    },
8215                    write: |settings_content, value| {
8216                        language_settings_field_mut(settings_content, value, |language, value| {
8217                            language.show_whitespaces = value;
8218                        })
8219                    },
8220                }),
8221                metadata: None,
8222                files: USER | PROJECT,
8223            }),
8224            SettingsPageItem::SettingItem(SettingItem {
8225                title: "Space Whitespace Indicator",
8226                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
8227                field: Box::new(
8228                    SettingField {
8229                        json_path: Some("languages.$(language).whitespace_map.space"),
8230                        pick: |settings_content| {
8231                            language_settings_field(settings_content, |language| {
8232                                language.whitespace_map.as_ref()?.space.as_ref()
8233                            })
8234                        },
8235                        write: |settings_content, value| {
8236                            language_settings_field_mut(
8237                                settings_content,
8238                                value,
8239                                |language, value| {
8240                                    language.whitespace_map.get_or_insert_default().space = value;
8241                                },
8242                            )
8243                        },
8244                    }
8245                    .unimplemented(),
8246                ),
8247                metadata: None,
8248                files: USER | PROJECT,
8249            }),
8250            SettingsPageItem::SettingItem(SettingItem {
8251                title: "Tab Whitespace Indicator",
8252                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
8253                field: Box::new(
8254                    SettingField {
8255                        json_path: Some("languages.$(language).whitespace_map.tab"),
8256                        pick: |settings_content| {
8257                            language_settings_field(settings_content, |language| {
8258                                language.whitespace_map.as_ref()?.tab.as_ref()
8259                            })
8260                        },
8261                        write: |settings_content, value| {
8262                            language_settings_field_mut(
8263                                settings_content,
8264                                value,
8265                                |language, value| {
8266                                    language.whitespace_map.get_or_insert_default().tab = value;
8267                                },
8268                            )
8269                        },
8270                    }
8271                    .unimplemented(),
8272                ),
8273                metadata: None,
8274                files: USER | PROJECT,
8275            }),
8276        ]
8277    }
8278
8279    fn completions_section() -> [SettingsPageItem; 7] {
8280        [
8281            SettingsPageItem::SectionHeader("Completions"),
8282            SettingsPageItem::SettingItem(SettingItem {
8283                title: "Show Completions On Input",
8284                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
8285                field: Box::new(SettingField {
8286                    json_path: Some("languages.$(language).show_completions_on_input"),
8287                    pick: |settings_content| {
8288                        language_settings_field(settings_content, |language| {
8289                            language.show_completions_on_input.as_ref()
8290                        })
8291                    },
8292                    write: |settings_content, value| {
8293                        language_settings_field_mut(settings_content, value, |language, value| {
8294                            language.show_completions_on_input = value;
8295                        })
8296                    },
8297                }),
8298                metadata: None,
8299                files: USER | PROJECT,
8300            }),
8301            SettingsPageItem::SettingItem(SettingItem {
8302                title: "Show Completion Documentation",
8303                description: "Whether to display inline and alongside documentation for items in the completions menu.",
8304                field: Box::new(SettingField {
8305                    json_path: Some("languages.$(language).show_completion_documentation"),
8306                    pick: |settings_content| {
8307                        language_settings_field(settings_content, |language| {
8308                            language.show_completion_documentation.as_ref()
8309                        })
8310                    },
8311                    write: |settings_content, value| {
8312                        language_settings_field_mut(settings_content, value, |language, value| {
8313                            language.show_completion_documentation = value;
8314                        })
8315                    },
8316                }),
8317                metadata: None,
8318                files: USER | PROJECT,
8319            }),
8320            SettingsPageItem::SettingItem(SettingItem {
8321                title: "Words",
8322                description: "Controls how words are completed.",
8323                field: Box::new(SettingField {
8324                    json_path: Some("languages.$(language).completions.words"),
8325                    pick: |settings_content| {
8326                        language_settings_field(settings_content, |language| {
8327                            language.completions.as_ref()?.words.as_ref()
8328                        })
8329                    },
8330                    write: |settings_content, value| {
8331                        language_settings_field_mut(settings_content, value, |language, value| {
8332                            language.completions.get_or_insert_default().words = value;
8333                        })
8334                    },
8335                }),
8336                metadata: None,
8337                files: USER | PROJECT,
8338            }),
8339            SettingsPageItem::SettingItem(SettingItem {
8340                title: "Words Min Length",
8341                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8342                field: Box::new(SettingField {
8343                    json_path: Some("languages.$(language).completions.words_min_length"),
8344                    pick: |settings_content| {
8345                        language_settings_field(settings_content, |language| {
8346                            language.completions.as_ref()?.words_min_length.as_ref()
8347                        })
8348                    },
8349                    write: |settings_content, value| {
8350                        language_settings_field_mut(settings_content, value, |language, value| {
8351                            language
8352                                .completions
8353                                .get_or_insert_default()
8354                                .words_min_length = value;
8355                        })
8356                    },
8357                }),
8358                metadata: None,
8359                files: USER | PROJECT,
8360            }),
8361            SettingsPageItem::SettingItem(SettingItem {
8362                title: "Completion Menu Scrollbar",
8363                description: "When to show the scrollbar in the completion menu.",
8364                field: Box::new(SettingField {
8365                    json_path: Some("editor.completion_menu_scrollbar"),
8366                    pick: |settings_content| {
8367                        settings_content.editor.completion_menu_scrollbar.as_ref()
8368                    },
8369                    write: |settings_content, value| {
8370                        settings_content.editor.completion_menu_scrollbar = value;
8371                    },
8372                }),
8373                metadata: None,
8374                files: USER,
8375            }),
8376            SettingsPageItem::SettingItem(SettingItem {
8377                title: "Completion Detail Alignment",
8378                description: "Whether to align detail text in code completions context menus left or right.",
8379                field: Box::new(SettingField {
8380                    json_path: Some("editor.completion_detail_alignment"),
8381                    pick: |settings_content| {
8382                        settings_content.editor.completion_detail_alignment.as_ref()
8383                    },
8384                    write: |settings_content, value| {
8385                        settings_content.editor.completion_detail_alignment = value;
8386                    },
8387                }),
8388                metadata: None,
8389                files: USER,
8390            }),
8391        ]
8392    }
8393
8394    fn inlay_hints_section() -> [SettingsPageItem; 10] {
8395        [
8396            SettingsPageItem::SectionHeader("Inlay Hints"),
8397            SettingsPageItem::SettingItem(SettingItem {
8398                title: "Enabled",
8399                description: "Global switch to toggle hints on and off.",
8400                field: Box::new(SettingField {
8401                    json_path: Some("languages.$(language).inlay_hints.enabled"),
8402                    pick: |settings_content| {
8403                        language_settings_field(settings_content, |language| {
8404                            language.inlay_hints.as_ref()?.enabled.as_ref()
8405                        })
8406                    },
8407                    write: |settings_content, value| {
8408                        language_settings_field_mut(settings_content, value, |language, value| {
8409                            language.inlay_hints.get_or_insert_default().enabled = value;
8410                        })
8411                    },
8412                }),
8413                metadata: None,
8414                files: USER | PROJECT,
8415            }),
8416            SettingsPageItem::SettingItem(SettingItem {
8417                title: "Show Value Hints",
8418                description: "Global switch to toggle inline values on and off when debugging.",
8419                field: Box::new(SettingField {
8420                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8421                    pick: |settings_content| {
8422                        language_settings_field(settings_content, |language| {
8423                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8424                        })
8425                    },
8426                    write: |settings_content, value| {
8427                        language_settings_field_mut(settings_content, value, |language, value| {
8428                            language
8429                                .inlay_hints
8430                                .get_or_insert_default()
8431                                .show_value_hints = value;
8432                        })
8433                    },
8434                }),
8435                metadata: None,
8436                files: USER | PROJECT,
8437            }),
8438            SettingsPageItem::SettingItem(SettingItem {
8439                title: "Show Type Hints",
8440                description: "Whether type hints should be shown.",
8441                field: Box::new(SettingField {
8442                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8443                    pick: |settings_content| {
8444                        language_settings_field(settings_content, |language| {
8445                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8446                        })
8447                    },
8448                    write: |settings_content, value| {
8449                        language_settings_field_mut(settings_content, value, |language, value| {
8450                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8451                        })
8452                    },
8453                }),
8454                metadata: None,
8455                files: USER | PROJECT,
8456            }),
8457            SettingsPageItem::SettingItem(SettingItem {
8458                title: "Show Parameter Hints",
8459                description: "Whether parameter hints should be shown.",
8460                field: Box::new(SettingField {
8461                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8462                    pick: |settings_content| {
8463                        language_settings_field(settings_content, |language| {
8464                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8465                        })
8466                    },
8467                    write: |settings_content, value| {
8468                        language_settings_field_mut(settings_content, value, |language, value| {
8469                            language
8470                                .inlay_hints
8471                                .get_or_insert_default()
8472                                .show_parameter_hints = value;
8473                        })
8474                    },
8475                }),
8476                metadata: None,
8477                files: USER | PROJECT,
8478            }),
8479            SettingsPageItem::SettingItem(SettingItem {
8480                title: "Show Other Hints",
8481                description: "Whether other hints should be shown.",
8482                field: Box::new(SettingField {
8483                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8484                    pick: |settings_content| {
8485                        language_settings_field(settings_content, |language| {
8486                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8487                        })
8488                    },
8489                    write: |settings_content, value| {
8490                        language_settings_field_mut(settings_content, value, |language, value| {
8491                            language
8492                                .inlay_hints
8493                                .get_or_insert_default()
8494                                .show_other_hints = value;
8495                        })
8496                    },
8497                }),
8498                metadata: None,
8499                files: USER | PROJECT,
8500            }),
8501            SettingsPageItem::SettingItem(SettingItem {
8502                title: "Show Background",
8503                description: "Show a background for inlay hints.",
8504                field: Box::new(SettingField {
8505                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8506                    pick: |settings_content| {
8507                        language_settings_field(settings_content, |language| {
8508                            language.inlay_hints.as_ref()?.show_background.as_ref()
8509                        })
8510                    },
8511                    write: |settings_content, value| {
8512                        language_settings_field_mut(settings_content, value, |language, value| {
8513                            language.inlay_hints.get_or_insert_default().show_background = value;
8514                        })
8515                    },
8516                }),
8517                metadata: None,
8518                files: USER | PROJECT,
8519            }),
8520            SettingsPageItem::SettingItem(SettingItem {
8521                title: "Edit Debounce Ms",
8522                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8523                field: Box::new(SettingField {
8524                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8525                    pick: |settings_content| {
8526                        language_settings_field(settings_content, |language| {
8527                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8528                        })
8529                    },
8530                    write: |settings_content, value| {
8531                        language_settings_field_mut(settings_content, value, |language, value| {
8532                            language
8533                                .inlay_hints
8534                                .get_or_insert_default()
8535                                .edit_debounce_ms = value;
8536                        })
8537                    },
8538                }),
8539                metadata: None,
8540                files: USER | PROJECT,
8541            }),
8542            SettingsPageItem::SettingItem(SettingItem {
8543                title: "Scroll Debounce Ms",
8544                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8545                field: Box::new(SettingField {
8546                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8547                    pick: |settings_content| {
8548                        language_settings_field(settings_content, |language| {
8549                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8550                        })
8551                    },
8552                    write: |settings_content, value| {
8553                        language_settings_field_mut(settings_content, value, |language, value| {
8554                            language
8555                                .inlay_hints
8556                                .get_or_insert_default()
8557                                .scroll_debounce_ms = value;
8558                        })
8559                    },
8560                }),
8561                metadata: None,
8562                files: USER | PROJECT,
8563            }),
8564            SettingsPageItem::SettingItem(SettingItem {
8565                title: "Toggle On Modifiers Press",
8566                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8567                field: Box::new(
8568                    SettingField {
8569                        json_path: Some(
8570                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8571                        ),
8572                        pick: |settings_content| {
8573                            language_settings_field(settings_content, |language| {
8574                                language
8575                                    .inlay_hints
8576                                    .as_ref()?
8577                                    .toggle_on_modifiers_press
8578                                    .as_ref()
8579                            })
8580                        },
8581                        write: |settings_content, value| {
8582                            language_settings_field_mut(
8583                                settings_content,
8584                                value,
8585                                |language, value| {
8586                                    language
8587                                        .inlay_hints
8588                                        .get_or_insert_default()
8589                                        .toggle_on_modifiers_press = value;
8590                                },
8591                            )
8592                        },
8593                    }
8594                    .unimplemented(),
8595                ),
8596                metadata: None,
8597                files: USER | PROJECT,
8598            }),
8599        ]
8600    }
8601
8602    fn tasks_section() -> [SettingsPageItem; 4] {
8603        [
8604            SettingsPageItem::SectionHeader("Tasks"),
8605            SettingsPageItem::SettingItem(SettingItem {
8606                title: "Enabled",
8607                description: "Whether tasks are enabled for this language.",
8608                field: Box::new(SettingField {
8609                    json_path: Some("languages.$(language).tasks.enabled"),
8610                    pick: |settings_content| {
8611                        language_settings_field(settings_content, |language| {
8612                            language.tasks.as_ref()?.enabled.as_ref()
8613                        })
8614                    },
8615                    write: |settings_content, value| {
8616                        language_settings_field_mut(settings_content, value, |language, value| {
8617                            language.tasks.get_or_insert_default().enabled = value;
8618                        })
8619                    },
8620                }),
8621                metadata: None,
8622                files: USER | PROJECT,
8623            }),
8624            SettingsPageItem::SettingItem(SettingItem {
8625                title: "Variables",
8626                description: "Extra task variables to set for a particular language.",
8627                field: Box::new(
8628                    SettingField {
8629                        json_path: Some("languages.$(language).tasks.variables"),
8630                        pick: |settings_content| {
8631                            language_settings_field(settings_content, |language| {
8632                                language.tasks.as_ref()?.variables.as_ref()
8633                            })
8634                        },
8635                        write: |settings_content, value| {
8636                            language_settings_field_mut(
8637                                settings_content,
8638                                value,
8639                                |language, value| {
8640                                    language.tasks.get_or_insert_default().variables = value;
8641                                },
8642                            )
8643                        },
8644                    }
8645                    .unimplemented(),
8646                ),
8647                metadata: None,
8648                files: USER | PROJECT,
8649            }),
8650            SettingsPageItem::SettingItem(SettingItem {
8651                title: "Prefer LSP",
8652                description: "Use LSP tasks over Zed language extension tasks.",
8653                field: Box::new(SettingField {
8654                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8655                    pick: |settings_content| {
8656                        language_settings_field(settings_content, |language| {
8657                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8658                        })
8659                    },
8660                    write: |settings_content, value| {
8661                        language_settings_field_mut(settings_content, value, |language, value| {
8662                            language.tasks.get_or_insert_default().prefer_lsp = value;
8663                        })
8664                    },
8665                }),
8666                metadata: None,
8667                files: USER | PROJECT,
8668            }),
8669        ]
8670    }
8671
8672    fn miscellaneous_section() -> [SettingsPageItem; 7] {
8673        [
8674            SettingsPageItem::SectionHeader("Miscellaneous"),
8675            SettingsPageItem::SettingItem(SettingItem {
8676                title: "Word Diff Enabled",
8677                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8678                field: Box::new(SettingField {
8679                    json_path: Some("languages.$(language).word_diff_enabled"),
8680                    pick: |settings_content| {
8681                        language_settings_field(settings_content, |language| {
8682                            language.word_diff_enabled.as_ref()
8683                        })
8684                    },
8685                    write: |settings_content, value| {
8686                        language_settings_field_mut(settings_content, value, |language, value| {
8687                            language.word_diff_enabled = value;
8688                        })
8689                    },
8690                }),
8691                metadata: None,
8692                files: USER | PROJECT,
8693            }),
8694            SettingsPageItem::SettingItem(SettingItem {
8695                title: "Debuggers",
8696                description: "Preferred debuggers for this language.",
8697                field: Box::new(
8698                    SettingField {
8699                        json_path: Some("languages.$(language).debuggers"),
8700                        pick: |settings_content| {
8701                            language_settings_field(settings_content, |language| {
8702                                language.debuggers.as_ref()
8703                            })
8704                        },
8705                        write: |settings_content, value| {
8706                            language_settings_field_mut(
8707                                settings_content,
8708                                value,
8709                                |language, value| {
8710                                    language.debuggers = value;
8711                                },
8712                            )
8713                        },
8714                    }
8715                    .unimplemented(),
8716                ),
8717                metadata: None,
8718                files: USER | PROJECT,
8719            }),
8720            SettingsPageItem::SettingItem(SettingItem {
8721                title: "Middle Click Paste",
8722                description: "Enable middle-click paste on Linux.",
8723                field: Box::new(SettingField {
8724                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8725                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8726                    write: |settings_content, value| {
8727                        settings_content.editor.middle_click_paste = value;
8728                    },
8729                }),
8730                metadata: None,
8731                files: USER,
8732            }),
8733            SettingsPageItem::SettingItem(SettingItem {
8734                title: "Extend Comment On Newline",
8735                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8736                field: Box::new(SettingField {
8737                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8738                    pick: |settings_content| {
8739                        language_settings_field(settings_content, |language| {
8740                            language.extend_comment_on_newline.as_ref()
8741                        })
8742                    },
8743                    write: |settings_content, value| {
8744                        language_settings_field_mut(settings_content, value, |language, value| {
8745                            language.extend_comment_on_newline = value;
8746                        })
8747                    },
8748                }),
8749                metadata: None,
8750                files: USER | PROJECT,
8751            }),
8752            SettingsPageItem::SettingItem(SettingItem {
8753                title: "Colorize Brackets",
8754                description: "Whether to colorize brackets in the editor.",
8755                field: Box::new(SettingField {
8756                    json_path: Some("languages.$(language).colorize_brackets"),
8757                    pick: |settings_content| {
8758                        language_settings_field(settings_content, |language| {
8759                            language.colorize_brackets.as_ref()
8760                        })
8761                    },
8762                    write: |settings_content, value| {
8763                        language_settings_field_mut(settings_content, value, |language, value| {
8764                            language.colorize_brackets = value;
8765                        })
8766                    },
8767                }),
8768                metadata: None,
8769                files: USER | PROJECT,
8770            }),
8771            SettingsPageItem::SettingItem(SettingItem {
8772                title: "Vim/Emacs Modeline Support",
8773                description: "Number of lines to search for modelines (set to 0 to disable).",
8774                field: Box::new(SettingField {
8775                    json_path: Some("modeline_lines"),
8776                    pick: |settings_content| settings_content.modeline_lines.as_ref(),
8777                    write: |settings_content, value| {
8778                        settings_content.modeline_lines = value;
8779                    },
8780                }),
8781                metadata: None,
8782                files: USER | PROJECT,
8783            }),
8784        ]
8785    }
8786
8787    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8788        [
8789            SettingsPageItem::SettingItem(SettingItem {
8790                title: "Image Viewer",
8791                description: "The unit for image file sizes.",
8792                field: Box::new(SettingField {
8793                    json_path: Some("image_viewer.unit"),
8794                    pick: |settings_content| {
8795                        settings_content
8796                            .image_viewer
8797                            .as_ref()
8798                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8799                    },
8800                    write: |settings_content, value| {
8801                        settings_content.image_viewer.get_or_insert_default().unit = value;
8802                    },
8803                }),
8804                metadata: None,
8805                files: USER,
8806            }),
8807            SettingsPageItem::SettingItem(SettingItem {
8808                title: "Auto Replace Emoji Shortcode",
8809                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8810                field: Box::new(SettingField {
8811                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8812                    pick: |settings_content| {
8813                        settings_content
8814                            .message_editor
8815                            .as_ref()
8816                            .and_then(|message_editor| {
8817                                message_editor.auto_replace_emoji_shortcode.as_ref()
8818                            })
8819                    },
8820                    write: |settings_content, value| {
8821                        settings_content
8822                            .message_editor
8823                            .get_or_insert_default()
8824                            .auto_replace_emoji_shortcode = value;
8825                    },
8826                }),
8827                metadata: None,
8828                files: USER,
8829            }),
8830            SettingsPageItem::SettingItem(SettingItem {
8831                title: "Drop Size Target",
8832                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8833                field: Box::new(SettingField {
8834                    json_path: Some("drop_target_size"),
8835                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8836                    write: |settings_content, value| {
8837                        settings_content.workspace.drop_target_size = value;
8838                    },
8839                }),
8840                metadata: None,
8841                files: USER,
8842            }),
8843        ]
8844    }
8845
8846    let is_global = active_language().is_none();
8847
8848    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8849        title: "LSP Document Colors",
8850        description: "How to render LSP color previews in the editor.",
8851        field: Box::new(SettingField {
8852            json_path: Some("lsp_document_colors"),
8853            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8854            write: |settings_content, value| {
8855                settings_content.editor.lsp_document_colors = value;
8856            },
8857        }),
8858        metadata: None,
8859        files: USER,
8860    })];
8861
8862    if is_global {
8863        concat_sections!(
8864            indentation_section(),
8865            wrapping_section(),
8866            indent_guides_section(),
8867            formatting_section(),
8868            autoclose_section(),
8869            whitespace_section(),
8870            completions_section(),
8871            inlay_hints_section(),
8872            lsp_document_colors_item,
8873            tasks_section(),
8874            miscellaneous_section(),
8875            global_only_miscellaneous_sub_section(),
8876        )
8877    } else {
8878        concat_sections!(
8879            indentation_section(),
8880            wrapping_section(),
8881            indent_guides_section(),
8882            formatting_section(),
8883            autoclose_section(),
8884            whitespace_section(),
8885            completions_section(),
8886            inlay_hints_section(),
8887            tasks_section(),
8888            miscellaneous_section(),
8889        )
8890    }
8891}
8892
8893/// LanguageSettings items that should be included in the "Languages & Tools" page
8894/// not the "Editor" page
8895fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8896    fn lsp_section() -> [SettingsPageItem; 8] {
8897        [
8898            SettingsPageItem::SectionHeader("LSP"),
8899            SettingsPageItem::SettingItem(SettingItem {
8900                title: "Enable Language Server",
8901                description: "Whether to use language servers to provide code intelligence.",
8902                field: Box::new(SettingField {
8903                    json_path: Some("languages.$(language).enable_language_server"),
8904                    pick: |settings_content| {
8905                        language_settings_field(settings_content, |language| {
8906                            language.enable_language_server.as_ref()
8907                        })
8908                    },
8909                    write: |settings_content, value| {
8910                        language_settings_field_mut(settings_content, value, |language, value| {
8911                            language.enable_language_server = value;
8912                        })
8913                    },
8914                }),
8915                metadata: None,
8916                files: USER | PROJECT,
8917            }),
8918            SettingsPageItem::SettingItem(SettingItem {
8919                title: "Language Servers",
8920                description: "The list of language servers to use (or disable) for this language.",
8921                field: Box::new(
8922                    SettingField {
8923                        json_path: Some("languages.$(language).language_servers"),
8924                        pick: |settings_content| {
8925                            language_settings_field(settings_content, |language| {
8926                                language.language_servers.as_ref()
8927                            })
8928                        },
8929                        write: |settings_content, value| {
8930                            language_settings_field_mut(
8931                                settings_content,
8932                                value,
8933                                |language, value| {
8934                                    language.language_servers = value;
8935                                },
8936                            )
8937                        },
8938                    }
8939                    .unimplemented(),
8940                ),
8941                metadata: None,
8942                files: USER | PROJECT,
8943            }),
8944            SettingsPageItem::SettingItem(SettingItem {
8945                title: "Linked Edits",
8946                description: "Whether to perform linked edits of associated ranges, if the LS supports it. For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.",
8947                field: Box::new(SettingField {
8948                    json_path: Some("languages.$(language).linked_edits"),
8949                    pick: |settings_content| {
8950                        language_settings_field(settings_content, |language| {
8951                            language.linked_edits.as_ref()
8952                        })
8953                    },
8954                    write: |settings_content, value| {
8955                        language_settings_field_mut(settings_content, value, |language, value| {
8956                            language.linked_edits = value;
8957                        })
8958                    },
8959                }),
8960                metadata: None,
8961                files: USER | PROJECT,
8962            }),
8963            SettingsPageItem::SettingItem(SettingItem {
8964                title: "Go To Definition Fallback",
8965                description: "Whether to follow-up empty Go to definition responses from the language server.",
8966                field: Box::new(SettingField {
8967                    json_path: Some("go_to_definition_fallback"),
8968                    pick: |settings_content| {
8969                        settings_content.editor.go_to_definition_fallback.as_ref()
8970                    },
8971                    write: |settings_content, value| {
8972                        settings_content.editor.go_to_definition_fallback = value;
8973                    },
8974                }),
8975                metadata: None,
8976                files: USER,
8977            }),
8978            SettingsPageItem::SettingItem(SettingItem {
8979                title: "Semantic Tokens",
8980                description: {
8981                    static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
8982                    DESCRIPTION.get_or_init(|| {
8983                        SemanticTokens::VARIANTS
8984                            .iter()
8985                            .filter_map(|v| {
8986                                v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
8987                            })
8988                            .join("\n")
8989                            .leak()
8990                    })
8991                },
8992                field: Box::new(SettingField {
8993                    json_path: Some("languages.$(language).semantic_tokens"),
8994                    pick: |settings_content| {
8995                        settings_content
8996                            .project
8997                            .all_languages
8998                            .defaults
8999                            .semantic_tokens
9000                            .as_ref()
9001                    },
9002                    write: |settings_content, value| {
9003                        settings_content
9004                            .project
9005                            .all_languages
9006                            .defaults
9007                            .semantic_tokens = value;
9008                    },
9009                }),
9010                metadata: None,
9011                files: USER | PROJECT,
9012            }),
9013            SettingsPageItem::SettingItem(SettingItem {
9014                title: "LSP Folding Ranges",
9015                description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
9016                field: Box::new(SettingField {
9017                    json_path: Some("languages.$(language).document_folding_ranges"),
9018                    pick: |settings_content| {
9019                        language_settings_field(settings_content, |language| {
9020                            language.document_folding_ranges.as_ref()
9021                        })
9022                    },
9023                    write: |settings_content, value| {
9024                        language_settings_field_mut(settings_content, value, |language, value| {
9025                            language.document_folding_ranges = value;
9026                        })
9027                    },
9028                }),
9029                metadata: None,
9030                files: USER | PROJECT,
9031            }),
9032            SettingsPageItem::SettingItem(SettingItem {
9033                title: "LSP Document Symbols",
9034                description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
9035                field: Box::new(SettingField {
9036                    json_path: Some("languages.$(language).document_symbols"),
9037                    pick: |settings_content| {
9038                        language_settings_field(settings_content, |language| {
9039                            language.document_symbols.as_ref()
9040                        })
9041                    },
9042                    write: |settings_content, value| {
9043                        language_settings_field_mut(settings_content, value, |language, value| {
9044                            language.document_symbols = value;
9045                        })
9046                    },
9047                }),
9048                metadata: None,
9049                files: USER | PROJECT,
9050            }),
9051        ]
9052    }
9053
9054    fn lsp_completions_section() -> [SettingsPageItem; 4] {
9055        [
9056            SettingsPageItem::SectionHeader("LSP Completions"),
9057            SettingsPageItem::SettingItem(SettingItem {
9058                title: "Enabled",
9059                description: "Whether to fetch LSP completions or not.",
9060                field: Box::new(SettingField {
9061                    json_path: Some("languages.$(language).completions.lsp"),
9062                    pick: |settings_content| {
9063                        language_settings_field(settings_content, |language| {
9064                            language.completions.as_ref()?.lsp.as_ref()
9065                        })
9066                    },
9067                    write: |settings_content, value| {
9068                        language_settings_field_mut(settings_content, value, |language, value| {
9069                            language.completions.get_or_insert_default().lsp = value;
9070                        })
9071                    },
9072                }),
9073                metadata: None,
9074                files: USER | PROJECT,
9075            }),
9076            SettingsPageItem::SettingItem(SettingItem {
9077                title: "Fetch Timeout (milliseconds)",
9078                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
9079                field: Box::new(SettingField {
9080                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
9081                    pick: |settings_content| {
9082                        language_settings_field(settings_content, |language| {
9083                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
9084                        })
9085                    },
9086                    write: |settings_content, value| {
9087                        language_settings_field_mut(settings_content, value, |language, value| {
9088                            language
9089                                .completions
9090                                .get_or_insert_default()
9091                                .lsp_fetch_timeout_ms = value;
9092                        })
9093                    },
9094                }),
9095                metadata: None,
9096                files: USER | PROJECT,
9097            }),
9098            SettingsPageItem::SettingItem(SettingItem {
9099                title: "Insert Mode",
9100                description: "Controls how LSP completions are inserted.",
9101                field: Box::new(SettingField {
9102                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
9103                    pick: |settings_content| {
9104                        language_settings_field(settings_content, |language| {
9105                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
9106                        })
9107                    },
9108                    write: |settings_content, value| {
9109                        language_settings_field_mut(settings_content, value, |language, value| {
9110                            language.completions.get_or_insert_default().lsp_insert_mode = value;
9111                        })
9112                    },
9113                }),
9114                metadata: None,
9115                files: USER | PROJECT,
9116            }),
9117        ]
9118    }
9119
9120    fn debugger_section() -> [SettingsPageItem; 2] {
9121        [
9122            SettingsPageItem::SectionHeader("Debuggers"),
9123            SettingsPageItem::SettingItem(SettingItem {
9124                title: "Debuggers",
9125                description: "Preferred debuggers for this language.",
9126                field: Box::new(
9127                    SettingField {
9128                        json_path: Some("languages.$(language).debuggers"),
9129                        pick: |settings_content| {
9130                            language_settings_field(settings_content, |language| {
9131                                language.debuggers.as_ref()
9132                            })
9133                        },
9134                        write: |settings_content, value| {
9135                            language_settings_field_mut(
9136                                settings_content,
9137                                value,
9138                                |language, value| {
9139                                    language.debuggers = value;
9140                                },
9141                            )
9142                        },
9143                    }
9144                    .unimplemented(),
9145                ),
9146                metadata: None,
9147                files: USER | PROJECT,
9148            }),
9149        ]
9150    }
9151
9152    fn prettier_section() -> [SettingsPageItem; 5] {
9153        [
9154            SettingsPageItem::SectionHeader("Prettier"),
9155            SettingsPageItem::SettingItem(SettingItem {
9156                title: "Allowed",
9157                description: "Enables or disables formatting with Prettier for a given language.",
9158                field: Box::new(SettingField {
9159                    json_path: Some("languages.$(language).prettier.allowed"),
9160                    pick: |settings_content| {
9161                        language_settings_field(settings_content, |language| {
9162                            language.prettier.as_ref()?.allowed.as_ref()
9163                        })
9164                    },
9165                    write: |settings_content, value| {
9166                        language_settings_field_mut(settings_content, value, |language, value| {
9167                            language.prettier.get_or_insert_default().allowed = value;
9168                        })
9169                    },
9170                }),
9171                metadata: None,
9172                files: USER | PROJECT,
9173            }),
9174            SettingsPageItem::SettingItem(SettingItem {
9175                title: "Parser",
9176                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
9177                field: Box::new(SettingField {
9178                    json_path: Some("languages.$(language).prettier.parser"),
9179                    pick: |settings_content| {
9180                        language_settings_field(settings_content, |language| {
9181                            language.prettier.as_ref()?.parser.as_ref()
9182                        })
9183                    },
9184                    write: |settings_content, value| {
9185                        language_settings_field_mut(settings_content, value, |language, value| {
9186                            language.prettier.get_or_insert_default().parser = value;
9187                        })
9188                    },
9189                }),
9190                metadata: None,
9191                files: USER | PROJECT,
9192            }),
9193            SettingsPageItem::SettingItem(SettingItem {
9194                title: "Plugins",
9195                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
9196                field: Box::new(
9197                    SettingField {
9198                        json_path: Some("languages.$(language).prettier.plugins"),
9199                        pick: |settings_content| {
9200                            language_settings_field(settings_content, |language| {
9201                                language.prettier.as_ref()?.plugins.as_ref()
9202                            })
9203                        },
9204                        write: |settings_content, value| {
9205                            language_settings_field_mut(
9206                                settings_content,
9207                                value,
9208                                |language, value| {
9209                                    language.prettier.get_or_insert_default().plugins = value;
9210                                },
9211                            )
9212                        },
9213                    }
9214                    .unimplemented(),
9215                ),
9216                metadata: None,
9217                files: USER | PROJECT,
9218            }),
9219            SettingsPageItem::SettingItem(SettingItem {
9220                title: "Options",
9221                description: "Default Prettier options, in the format as in package.json section for Prettier.",
9222                field: Box::new(
9223                    SettingField {
9224                        json_path: Some("languages.$(language).prettier.options"),
9225                        pick: |settings_content| {
9226                            language_settings_field(settings_content, |language| {
9227                                language.prettier.as_ref()?.options.as_ref()
9228                            })
9229                        },
9230                        write: |settings_content, value| {
9231                            language_settings_field_mut(
9232                                settings_content,
9233                                value,
9234                                |language, value| {
9235                                    language.prettier.get_or_insert_default().options = value;
9236                                },
9237                            )
9238                        },
9239                    }
9240                    .unimplemented(),
9241                ),
9242                metadata: None,
9243                files: USER | PROJECT,
9244            }),
9245        ]
9246    }
9247
9248    concat_sections!(
9249        lsp_section(),
9250        lsp_completions_section(),
9251        debugger_section(),
9252        prettier_section(),
9253    )
9254}
9255
9256fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
9257    [
9258        SettingsPageItem::SectionHeader("Edit Predictions"),
9259        SettingsPageItem::SubPageLink(SubPageLink {
9260            title: "Configure Providers".into(),
9261            r#type: Default::default(),
9262            json_path: Some("edit_predictions.providers"),
9263            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
9264            in_json: false,
9265            files: USER,
9266            render: render_edit_prediction_setup_page
9267        }),
9268        SettingsPageItem::SettingItem(SettingItem {
9269            title: "Show Edit Predictions",
9270            description: "Controls whether edit predictions are shown immediately or manually.",
9271            field: Box::new(SettingField {
9272                json_path: Some("languages.$(language).show_edit_predictions"),
9273                pick: |settings_content| {
9274                    language_settings_field(settings_content, |language| {
9275                        language.show_edit_predictions.as_ref()
9276                    })
9277                },
9278                write: |settings_content, value| {
9279                    language_settings_field_mut(settings_content, value, |language, value| {
9280                        language.show_edit_predictions = value;
9281                    })
9282                },
9283            }),
9284            metadata: None,
9285            files: USER | PROJECT,
9286        }),
9287        SettingsPageItem::SettingItem(SettingItem {
9288            title: "Disable in Language Scopes",
9289            description: "Controls whether edit predictions are shown in the given language scopes.",
9290            field: Box::new(
9291                SettingField {
9292                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
9293                    pick: |settings_content| {
9294                        language_settings_field(settings_content, |language| {
9295                            language.edit_predictions_disabled_in.as_ref()
9296                        })
9297                    },
9298                    write: |settings_content, value| {
9299                        language_settings_field_mut(settings_content, value, |language, value| {
9300                            language.edit_predictions_disabled_in = value;
9301                        })
9302                    },
9303                }
9304                .unimplemented(),
9305            ),
9306            metadata: None,
9307            files: USER | PROJECT,
9308        }),
9309    ]
9310}
9311
9312fn show_scrollbar_or_editor(
9313    settings_content: &SettingsContent,
9314    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
9315) -> Option<&settings::ShowScrollbar> {
9316    show(settings_content).or(settings_content
9317        .editor
9318        .scrollbar
9319        .as_ref()
9320        .and_then(|scrollbar| scrollbar.show.as_ref()))
9321}
9322
9323fn dynamic_variants<T>() -> &'static [T::Discriminant]
9324where
9325    T: strum::IntoDiscriminant,
9326    T::Discriminant: strum::VariantArray,
9327{
9328    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
9329}
9330
9331/// Updates the `vim_mode` setting, disabling `helix_mode` if present and
9332/// `vim_mode` is being enabled.
9333fn write_vim_mode(settings: &mut SettingsContent, value: Option<bool>) {
9334    if value == Some(true) && settings.helix_mode == Some(true) {
9335        settings.helix_mode = Some(false);
9336    }
9337    settings.vim_mode = value;
9338}
9339
9340/// Updates the `helix_mode` setting, disabling `vim_mode` if present and
9341/// `helix_mode` is being enabled.
9342fn write_helix_mode(settings: &mut SettingsContent, value: Option<bool>) {
9343    if value == Some(true) && settings.vim_mode == Some(true) {
9344        settings.vim_mode = Some(false);
9345    }
9346    settings.helix_mode = value;
9347}
9348
9349#[cfg(test)]
9350mod tests {
9351    use super::*;
9352
9353    #[test]
9354    fn test_write_vim_helix_mode() {
9355        // Enabling vim mode while `vim_mode` and `helix_mode` are not yet set
9356        // should only update the `vim_mode` setting.
9357        let mut settings = SettingsContent::default();
9358        write_vim_mode(&mut settings, Some(true));
9359        assert_eq!(settings.vim_mode, Some(true));
9360        assert_eq!(settings.helix_mode, None);
9361
9362        // Enabling helix mode while `vim_mode` and `helix_mode` are not yet set
9363        // should only update the `helix_mode` setting.
9364        let mut settings = SettingsContent::default();
9365        write_helix_mode(&mut settings, Some(true));
9366        assert_eq!(settings.helix_mode, Some(true));
9367        assert_eq!(settings.vim_mode, None);
9368
9369        // Disabling helix mode should only touch `helix_mode` setting when
9370        // `vim_mode` is not set.
9371        write_helix_mode(&mut settings, Some(false));
9372        assert_eq!(settings.helix_mode, Some(false));
9373        assert_eq!(settings.vim_mode, None);
9374
9375        // Enabling vim mode should update `vim_mode` but leave `helix_mode`
9376        // untouched.
9377        write_vim_mode(&mut settings, Some(true));
9378        assert_eq!(settings.vim_mode, Some(true));
9379        assert_eq!(settings.helix_mode, Some(false));
9380
9381        // Enabling helix mode should update `helix_mode` and disable
9382        // `vim_mode`.
9383        write_helix_mode(&mut settings, Some(true));
9384        assert_eq!(settings.helix_mode, Some(true));
9385        assert_eq!(settings.vim_mode, Some(false));
9386
9387        // Enabling vim mode should update `vim_mode` and disable
9388        // `helix_mode`.
9389        write_vim_mode(&mut settings, Some(true));
9390        assert_eq!(settings.vim_mode, Some(true));
9391        assert_eq!(settings.helix_mode, Some(false));
9392    }
9393}