page_data.rs

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