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; 28] {
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: "Auto Open Files On Create",
4953                description: "Whether to automatically open newly created files in the editor.",
4954                field: Box::new(SettingField {
4955                    json_path: Some("project_panel.auto_open.on_create"),
4956                    pick: |settings_content| {
4957                        settings_content
4958                            .project_panel
4959                            .as_ref()?
4960                            .auto_open
4961                            .as_ref()?
4962                            .on_create
4963                            .as_ref()
4964                    },
4965                    write: |settings_content, value| {
4966                        settings_content
4967                            .project_panel
4968                            .get_or_insert_default()
4969                            .auto_open
4970                            .get_or_insert_default()
4971                            .on_create = value;
4972                    },
4973                }),
4974                metadata: None,
4975                files: USER,
4976            }),
4977            SettingsPageItem::SettingItem(SettingItem {
4978                title: "Auto Open Files On Paste",
4979                description: "Whether to automatically open files after pasting or duplicating them.",
4980                field: Box::new(SettingField {
4981                    json_path: Some("project_panel.auto_open.on_paste"),
4982                    pick: |settings_content| {
4983                        settings_content
4984                            .project_panel
4985                            .as_ref()?
4986                            .auto_open
4987                            .as_ref()?
4988                            .on_paste
4989                            .as_ref()
4990                    },
4991                    write: |settings_content, value| {
4992                        settings_content
4993                            .project_panel
4994                            .get_or_insert_default()
4995                            .auto_open
4996                            .get_or_insert_default()
4997                            .on_paste = value;
4998                    },
4999                }),
5000                metadata: None,
5001                files: USER,
5002            }),
5003            SettingsPageItem::SettingItem(SettingItem {
5004                title: "Auto Open Files On Drop",
5005                description: "Whether to automatically open files dropped from external sources.",
5006                field: Box::new(SettingField {
5007                    json_path: Some("project_panel.auto_open.on_drop"),
5008                    pick: |settings_content| {
5009                        settings_content
5010                            .project_panel
5011                            .as_ref()?
5012                            .auto_open
5013                            .as_ref()?
5014                            .on_drop
5015                            .as_ref()
5016                    },
5017                    write: |settings_content, value| {
5018                        settings_content
5019                            .project_panel
5020                            .get_or_insert_default()
5021                            .auto_open
5022                            .get_or_insert_default()
5023                            .on_drop = value;
5024                    },
5025                }),
5026                metadata: None,
5027                files: USER,
5028            }),
5029            SettingsPageItem::SettingItem(SettingItem {
5030                title: "Hidden Files",
5031                description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
5032                field: Box::new(
5033                    SettingField {
5034                        json_path: Some("worktree.hidden_files"),
5035                        pick: |settings_content| {
5036                            settings_content.project.worktree.hidden_files.as_ref()
5037                        },
5038                        write: |settings_content, value| {
5039                            settings_content.project.worktree.hidden_files = value;
5040                        },
5041                    }
5042                    .unimplemented(),
5043                ),
5044                metadata: None,
5045                files: USER,
5046            }),
5047        ]
5048    }
5049
5050    fn terminal_panel_section() -> [SettingsPageItem; 4] {
5051        [
5052            SettingsPageItem::SectionHeader("Terminal Panel"),
5053            SettingsPageItem::SettingItem(SettingItem {
5054                title: "Terminal Dock",
5055                description: "Where to dock the terminal panel.",
5056                field: Box::new(SettingField {
5057                    json_path: Some("terminal.dock"),
5058                    pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
5059                    write: |settings_content, value| {
5060                        settings_content.terminal.get_or_insert_default().dock = value;
5061                    },
5062                }),
5063                metadata: None,
5064                files: USER,
5065            }),
5066            SettingsPageItem::SettingItem(SettingItem {
5067                title: "Terminal Panel Flexible Sizing",
5068                description: "Whether the terminal panel should use flexible (proportional) sizing when docked to the left or right.",
5069                field: Box::new(SettingField {
5070                    json_path: Some("terminal.flexible"),
5071                    pick: |settings_content| settings_content.terminal.as_ref()?.flexible.as_ref(),
5072                    write: |settings_content, value| {
5073                        settings_content.terminal.get_or_insert_default().flexible = value;
5074                    },
5075                }),
5076                metadata: None,
5077                files: USER,
5078            }),
5079            SettingsPageItem::SettingItem(SettingItem {
5080                title: "Show Count Badge",
5081                description: "Show a badge on the terminal panel icon with the count of open terminals.",
5082                field: Box::new(SettingField {
5083                    json_path: Some("terminal.show_count_badge"),
5084                    pick: |settings_content| {
5085                        settings_content
5086                            .terminal
5087                            .as_ref()?
5088                            .show_count_badge
5089                            .as_ref()
5090                    },
5091                    write: |settings_content, value| {
5092                        settings_content
5093                            .terminal
5094                            .get_or_insert_default()
5095                            .show_count_badge = value;
5096                    },
5097                }),
5098                metadata: None,
5099                files: USER,
5100            }),
5101        ]
5102    }
5103
5104    fn outline_panel_section() -> [SettingsPageItem; 11] {
5105        [
5106            SettingsPageItem::SectionHeader("Outline Panel"),
5107            SettingsPageItem::SettingItem(SettingItem {
5108                title: "Outline Panel Button",
5109                description: "Show the outline panel button in the status bar.",
5110                field: Box::new(SettingField {
5111                    json_path: Some("outline_panel.button"),
5112                    pick: |settings_content| {
5113                        settings_content.outline_panel.as_ref()?.button.as_ref()
5114                    },
5115                    write: |settings_content, value| {
5116                        settings_content
5117                            .outline_panel
5118                            .get_or_insert_default()
5119                            .button = value;
5120                    },
5121                }),
5122                metadata: None,
5123                files: USER,
5124            }),
5125            SettingsPageItem::SettingItem(SettingItem {
5126                title: "Outline Panel Dock",
5127                description: "Where to dock the outline panel.",
5128                field: Box::new(SettingField {
5129                    json_path: Some("outline_panel.dock"),
5130                    pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
5131                    write: |settings_content, value| {
5132                        settings_content.outline_panel.get_or_insert_default().dock = value;
5133                    },
5134                }),
5135                metadata: None,
5136                files: USER,
5137            }),
5138            SettingsPageItem::SettingItem(SettingItem {
5139                title: "Outline Panel Default Width",
5140                description: "Default width of the outline panel in pixels.",
5141                field: Box::new(SettingField {
5142                    json_path: Some("outline_panel.default_width"),
5143                    pick: |settings_content| {
5144                        settings_content
5145                            .outline_panel
5146                            .as_ref()?
5147                            .default_width
5148                            .as_ref()
5149                    },
5150                    write: |settings_content, value| {
5151                        settings_content
5152                            .outline_panel
5153                            .get_or_insert_default()
5154                            .default_width = value;
5155                    },
5156                }),
5157                metadata: None,
5158                files: USER,
5159            }),
5160            SettingsPageItem::SettingItem(SettingItem {
5161                title: "File Icons",
5162                description: "Show file icons in the outline panel.",
5163                field: Box::new(SettingField {
5164                    json_path: Some("outline_panel.file_icons"),
5165                    pick: |settings_content| {
5166                        settings_content.outline_panel.as_ref()?.file_icons.as_ref()
5167                    },
5168                    write: |settings_content, value| {
5169                        settings_content
5170                            .outline_panel
5171                            .get_or_insert_default()
5172                            .file_icons = value;
5173                    },
5174                }),
5175                metadata: None,
5176                files: USER,
5177            }),
5178            SettingsPageItem::SettingItem(SettingItem {
5179                title: "Folder Icons",
5180                description: "Whether to show folder icons or chevrons for directories in the outline panel.",
5181                field: Box::new(SettingField {
5182                    json_path: Some("outline_panel.folder_icons"),
5183                    pick: |settings_content| {
5184                        settings_content
5185                            .outline_panel
5186                            .as_ref()?
5187                            .folder_icons
5188                            .as_ref()
5189                    },
5190                    write: |settings_content, value| {
5191                        settings_content
5192                            .outline_panel
5193                            .get_or_insert_default()
5194                            .folder_icons = value;
5195                    },
5196                }),
5197                metadata: None,
5198                files: USER,
5199            }),
5200            SettingsPageItem::SettingItem(SettingItem {
5201                title: "Git Status",
5202                description: "Show the Git status in the outline panel.",
5203                field: Box::new(SettingField {
5204                    json_path: Some("outline_panel.git_status"),
5205                    pick: |settings_content| {
5206                        settings_content.outline_panel.as_ref()?.git_status.as_ref()
5207                    },
5208                    write: |settings_content, value| {
5209                        settings_content
5210                            .outline_panel
5211                            .get_or_insert_default()
5212                            .git_status = value;
5213                    },
5214                }),
5215                metadata: None,
5216                files: USER,
5217            }),
5218            SettingsPageItem::SettingItem(SettingItem {
5219                title: "Indent Size",
5220                description: "Amount of indentation for nested items.",
5221                field: Box::new(SettingField {
5222                    json_path: Some("outline_panel.indent_size"),
5223                    pick: |settings_content| {
5224                        settings_content
5225                            .outline_panel
5226                            .as_ref()?
5227                            .indent_size
5228                            .as_ref()
5229                    },
5230                    write: |settings_content, value| {
5231                        settings_content
5232                            .outline_panel
5233                            .get_or_insert_default()
5234                            .indent_size = value;
5235                    },
5236                }),
5237                metadata: None,
5238                files: USER,
5239            }),
5240            SettingsPageItem::SettingItem(SettingItem {
5241                title: "Auto Reveal Entries",
5242                description: "Whether to reveal when a corresponding outline entry becomes active.",
5243                field: Box::new(SettingField {
5244                    json_path: Some("outline_panel.auto_reveal_entries"),
5245                    pick: |settings_content| {
5246                        settings_content
5247                            .outline_panel
5248                            .as_ref()?
5249                            .auto_reveal_entries
5250                            .as_ref()
5251                    },
5252                    write: |settings_content, value| {
5253                        settings_content
5254                            .outline_panel
5255                            .get_or_insert_default()
5256                            .auto_reveal_entries = value;
5257                    },
5258                }),
5259                metadata: None,
5260                files: USER,
5261            }),
5262            SettingsPageItem::SettingItem(SettingItem {
5263                title: "Auto Fold Directories",
5264                description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
5265                field: Box::new(SettingField {
5266                    json_path: Some("outline_panel.auto_fold_dirs"),
5267                    pick: |settings_content| {
5268                        settings_content
5269                            .outline_panel
5270                            .as_ref()?
5271                            .auto_fold_dirs
5272                            .as_ref()
5273                    },
5274                    write: |settings_content, value| {
5275                        settings_content
5276                            .outline_panel
5277                            .get_or_insert_default()
5278                            .auto_fold_dirs = value;
5279                    },
5280                }),
5281                metadata: None,
5282                files: USER,
5283            }),
5284            SettingsPageItem::SettingItem(SettingItem {
5285                files: USER,
5286                title: "Show Indent Guides",
5287                description: "When to show indent guides in the outline panel.",
5288                field: Box::new(SettingField {
5289                    json_path: Some("outline_panel.indent_guides.show"),
5290                    pick: |settings_content| {
5291                        settings_content
5292                            .outline_panel
5293                            .as_ref()?
5294                            .indent_guides
5295                            .as_ref()?
5296                            .show
5297                            .as_ref()
5298                    },
5299                    write: |settings_content, value| {
5300                        settings_content
5301                            .outline_panel
5302                            .get_or_insert_default()
5303                            .indent_guides
5304                            .get_or_insert_default()
5305                            .show = value;
5306                    },
5307                }),
5308                metadata: None,
5309            }),
5310        ]
5311    }
5312
5313    fn git_panel_section() -> [SettingsPageItem; 14] {
5314        [
5315            SettingsPageItem::SectionHeader("Git Panel"),
5316            SettingsPageItem::SettingItem(SettingItem {
5317                title: "Git Panel Button",
5318                description: "Show the Git panel button in the status bar.",
5319                field: Box::new(SettingField {
5320                    json_path: Some("git_panel.button"),
5321                    pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5322                    write: |settings_content, value| {
5323                        settings_content.git_panel.get_or_insert_default().button = value;
5324                    },
5325                }),
5326                metadata: None,
5327                files: USER,
5328            }),
5329            SettingsPageItem::SettingItem(SettingItem {
5330                title: "Git Panel Dock",
5331                description: "Where to dock the Git panel.",
5332                field: Box::new(SettingField {
5333                    json_path: Some("git_panel.dock"),
5334                    pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5335                    write: |settings_content, value| {
5336                        settings_content.git_panel.get_or_insert_default().dock = value;
5337                    },
5338                }),
5339                metadata: None,
5340                files: USER,
5341            }),
5342            SettingsPageItem::SettingItem(SettingItem {
5343                title: "Git Panel Default Width",
5344                description: "Default width of the Git panel in pixels.",
5345                field: Box::new(SettingField {
5346                    json_path: Some("git_panel.default_width"),
5347                    pick: |settings_content| {
5348                        settings_content.git_panel.as_ref()?.default_width.as_ref()
5349                    },
5350                    write: |settings_content, value| {
5351                        settings_content
5352                            .git_panel
5353                            .get_or_insert_default()
5354                            .default_width = value;
5355                    },
5356                }),
5357                metadata: None,
5358                files: USER,
5359            }),
5360            SettingsPageItem::SettingItem(SettingItem {
5361                title: "Git Panel Status Style",
5362                description: "How entry statuses are displayed.",
5363                field: Box::new(SettingField {
5364                    json_path: Some("git_panel.status_style"),
5365                    pick: |settings_content| {
5366                        settings_content.git_panel.as_ref()?.status_style.as_ref()
5367                    },
5368                    write: |settings_content, value| {
5369                        settings_content
5370                            .git_panel
5371                            .get_or_insert_default()
5372                            .status_style = value;
5373                    },
5374                }),
5375                metadata: None,
5376                files: USER,
5377            }),
5378            SettingsPageItem::SettingItem(SettingItem {
5379                title: "Fallback Branch Name",
5380                description: "Default branch name will be when init.defaultbranch is not set in Git.",
5381                field: Box::new(SettingField {
5382                    json_path: Some("git_panel.fallback_branch_name"),
5383                    pick: |settings_content| {
5384                        settings_content
5385                            .git_panel
5386                            .as_ref()?
5387                            .fallback_branch_name
5388                            .as_ref()
5389                    },
5390                    write: |settings_content, value| {
5391                        settings_content
5392                            .git_panel
5393                            .get_or_insert_default()
5394                            .fallback_branch_name = value;
5395                    },
5396                }),
5397                metadata: None,
5398                files: USER,
5399            }),
5400            SettingsPageItem::SettingItem(SettingItem {
5401                title: "Sort By Path",
5402                description: "Enable to sort entries in the panel by path, disable to sort by status.",
5403                field: Box::new(SettingField {
5404                    json_path: Some("git_panel.sort_by_path"),
5405                    pick: |settings_content| {
5406                        settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5407                    },
5408                    write: |settings_content, value| {
5409                        settings_content
5410                            .git_panel
5411                            .get_or_insert_default()
5412                            .sort_by_path = value;
5413                    },
5414                }),
5415                metadata: None,
5416                files: USER,
5417            }),
5418            SettingsPageItem::SettingItem(SettingItem {
5419                title: "Collapse Untracked Diff",
5420                description: "Whether to collapse untracked files in the diff panel.",
5421                field: Box::new(SettingField {
5422                    json_path: Some("git_panel.collapse_untracked_diff"),
5423                    pick: |settings_content| {
5424                        settings_content
5425                            .git_panel
5426                            .as_ref()?
5427                            .collapse_untracked_diff
5428                            .as_ref()
5429                    },
5430                    write: |settings_content, value| {
5431                        settings_content
5432                            .git_panel
5433                            .get_or_insert_default()
5434                            .collapse_untracked_diff = value;
5435                    },
5436                }),
5437                metadata: None,
5438                files: USER,
5439            }),
5440            SettingsPageItem::SettingItem(SettingItem {
5441                title: "Tree View",
5442                description: "Enable to show entries in tree view list, disable to show in flat view list.",
5443                field: Box::new(SettingField {
5444                    json_path: Some("git_panel.tree_view"),
5445                    pick: |settings_content| {
5446                        settings_content.git_panel.as_ref()?.tree_view.as_ref()
5447                    },
5448                    write: |settings_content, value| {
5449                        settings_content.git_panel.get_or_insert_default().tree_view = value;
5450                    },
5451                }),
5452                metadata: None,
5453                files: USER,
5454            }),
5455            SettingsPageItem::SettingItem(SettingItem {
5456                title: "File Icons",
5457                description: "Show file icons next to the Git status icon.",
5458                field: Box::new(SettingField {
5459                    json_path: Some("git_panel.file_icons"),
5460                    pick: |settings_content| {
5461                        settings_content.git_panel.as_ref()?.file_icons.as_ref()
5462                    },
5463                    write: |settings_content, value| {
5464                        settings_content
5465                            .git_panel
5466                            .get_or_insert_default()
5467                            .file_icons = value;
5468                    },
5469                }),
5470                metadata: None,
5471                files: USER,
5472            }),
5473            SettingsPageItem::SettingItem(SettingItem {
5474                title: "Folder Icons",
5475                description: "Whether to show folder icons or chevrons for directories in the git panel.",
5476                field: Box::new(SettingField {
5477                    json_path: Some("git_panel.folder_icons"),
5478                    pick: |settings_content| {
5479                        settings_content.git_panel.as_ref()?.folder_icons.as_ref()
5480                    },
5481                    write: |settings_content, value| {
5482                        settings_content
5483                            .git_panel
5484                            .get_or_insert_default()
5485                            .folder_icons = value;
5486                    },
5487                }),
5488                metadata: None,
5489                files: USER,
5490            }),
5491            SettingsPageItem::SettingItem(SettingItem {
5492                title: "Diff Stats",
5493                description: "Whether to show the addition/deletion change count next to each file in the Git panel.",
5494                field: Box::new(SettingField {
5495                    json_path: Some("git_panel.diff_stats"),
5496                    pick: |settings_content| {
5497                        settings_content.git_panel.as_ref()?.diff_stats.as_ref()
5498                    },
5499                    write: |settings_content, value| {
5500                        settings_content
5501                            .git_panel
5502                            .get_or_insert_default()
5503                            .diff_stats = value;
5504                    },
5505                }),
5506                metadata: None,
5507                files: USER,
5508            }),
5509            SettingsPageItem::SettingItem(SettingItem {
5510                title: "Show Count Badge",
5511                description: "Whether to show a badge on the git panel icon with the count of uncommitted changes.",
5512                field: Box::new(SettingField {
5513                    json_path: Some("git_panel.show_count_badge"),
5514                    pick: |settings_content| {
5515                        settings_content
5516                            .git_panel
5517                            .as_ref()?
5518                            .show_count_badge
5519                            .as_ref()
5520                    },
5521                    write: |settings_content, value| {
5522                        settings_content
5523                            .git_panel
5524                            .get_or_insert_default()
5525                            .show_count_badge = value;
5526                    },
5527                }),
5528                metadata: None,
5529                files: USER,
5530            }),
5531            SettingsPageItem::SettingItem(SettingItem {
5532                title: "Scroll Bar",
5533                description: "How and when the scrollbar should be displayed.",
5534                field: Box::new(SettingField {
5535                    json_path: Some("git_panel.scrollbar.show"),
5536                    pick: |settings_content| {
5537                        show_scrollbar_or_editor(settings_content, |settings_content| {
5538                            settings_content
5539                                .git_panel
5540                                .as_ref()?
5541                                .scrollbar
5542                                .as_ref()?
5543                                .show
5544                                .as_ref()
5545                        })
5546                    },
5547                    write: |settings_content, value| {
5548                        settings_content
5549                            .git_panel
5550                            .get_or_insert_default()
5551                            .scrollbar
5552                            .get_or_insert_default()
5553                            .show = value;
5554                    },
5555                }),
5556                metadata: None,
5557                files: USER,
5558            }),
5559        ]
5560    }
5561
5562    fn debugger_panel_section() -> [SettingsPageItem; 2] {
5563        [
5564            SettingsPageItem::SectionHeader("Debugger Panel"),
5565            SettingsPageItem::SettingItem(SettingItem {
5566                title: "Debugger Panel Dock",
5567                description: "The dock position of the debug panel.",
5568                field: Box::new(SettingField {
5569                    json_path: Some("debugger.dock"),
5570                    pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5571                    write: |settings_content, value| {
5572                        settings_content.debugger.get_or_insert_default().dock = value;
5573                    },
5574                }),
5575                metadata: None,
5576                files: USER,
5577            }),
5578        ]
5579    }
5580
5581    fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5582        [
5583            SettingsPageItem::SectionHeader("Collaboration Panel"),
5584            SettingsPageItem::SettingItem(SettingItem {
5585                title: "Collaboration Panel Button",
5586                description: "Show the collaboration panel button in the status bar.",
5587                field: Box::new(SettingField {
5588                    json_path: Some("collaboration_panel.button"),
5589                    pick: |settings_content| {
5590                        settings_content
5591                            .collaboration_panel
5592                            .as_ref()?
5593                            .button
5594                            .as_ref()
5595                    },
5596                    write: |settings_content, value| {
5597                        settings_content
5598                            .collaboration_panel
5599                            .get_or_insert_default()
5600                            .button = value;
5601                    },
5602                }),
5603                metadata: None,
5604                files: USER,
5605            }),
5606            SettingsPageItem::SettingItem(SettingItem {
5607                title: "Collaboration Panel Dock",
5608                description: "Where to dock the collaboration panel.",
5609                field: Box::new(SettingField {
5610                    json_path: Some("collaboration_panel.dock"),
5611                    pick: |settings_content| {
5612                        settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5613                    },
5614                    write: |settings_content, value| {
5615                        settings_content
5616                            .collaboration_panel
5617                            .get_or_insert_default()
5618                            .dock = value;
5619                    },
5620                }),
5621                metadata: None,
5622                files: USER,
5623            }),
5624            SettingsPageItem::SettingItem(SettingItem {
5625                title: "Collaboration Panel Default Width",
5626                description: "Default width of the collaboration panel in pixels.",
5627                field: Box::new(SettingField {
5628                    json_path: Some("collaboration_panel.dock"),
5629                    pick: |settings_content| {
5630                        settings_content
5631                            .collaboration_panel
5632                            .as_ref()?
5633                            .default_width
5634                            .as_ref()
5635                    },
5636                    write: |settings_content, value| {
5637                        settings_content
5638                            .collaboration_panel
5639                            .get_or_insert_default()
5640                            .default_width = value;
5641                    },
5642                }),
5643                metadata: None,
5644                files: USER,
5645            }),
5646        ]
5647    }
5648
5649    fn agent_panel_section() -> [SettingsPageItem; 7] {
5650        [
5651            SettingsPageItem::SectionHeader("Agent Panel"),
5652            SettingsPageItem::SettingItem(SettingItem {
5653                title: "Agent Panel Button",
5654                description: "Whether to show the agent panel button in the status bar.",
5655                field: Box::new(SettingField {
5656                    json_path: Some("agent.button"),
5657                    pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5658                    write: |settings_content, value| {
5659                        settings_content.agent.get_or_insert_default().button = value;
5660                    },
5661                }),
5662                metadata: None,
5663                files: USER,
5664            }),
5665            SettingsPageItem::SettingItem(SettingItem {
5666                title: "Agent Panel Dock",
5667                description: "Where to dock the agent panel.",
5668                field: Box::new(SettingField {
5669                    json_path: Some("agent.dock"),
5670                    pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5671                    write: |settings_content, value| {
5672                        settings_content.agent.get_or_insert_default().dock = value;
5673                    },
5674                }),
5675                metadata: None,
5676                files: USER,
5677            }),
5678            SettingsPageItem::SettingItem(SettingItem {
5679                title: "Agent Panel Flexible Sizing",
5680                description: "Whether the agent panel should use flexible (proportional) sizing when docked to the left or right.",
5681                field: Box::new(SettingField {
5682                    json_path: Some("agent.flexible"),
5683                    pick: |settings_content| settings_content.agent.as_ref()?.flexible.as_ref(),
5684                    write: |settings_content, value| {
5685                        settings_content.agent.get_or_insert_default().flexible = value;
5686                    },
5687                }),
5688                metadata: None,
5689                files: USER,
5690            }),
5691            SettingsPageItem::SettingItem(SettingItem {
5692                title: "Agent Panel Default Width",
5693                description: "Default width when the agent panel is docked to the left or right.",
5694                field: Box::new(SettingField {
5695                    json_path: Some("agent.default_width"),
5696                    pick: |settings_content| {
5697                        settings_content.agent.as_ref()?.default_width.as_ref()
5698                    },
5699                    write: |settings_content, value| {
5700                        settings_content.agent.get_or_insert_default().default_width = value;
5701                    },
5702                }),
5703                metadata: None,
5704                files: USER,
5705            }),
5706            SettingsPageItem::SettingItem(SettingItem {
5707                title: "Agent Panel Default Height",
5708                description: "Default height when the agent panel is docked to the bottom.",
5709                field: Box::new(SettingField {
5710                    json_path: Some("agent.default_height"),
5711                    pick: |settings_content| {
5712                        settings_content.agent.as_ref()?.default_height.as_ref()
5713                    },
5714                    write: |settings_content, value| {
5715                        settings_content
5716                            .agent
5717                            .get_or_insert_default()
5718                            .default_height = value;
5719                    },
5720                }),
5721                metadata: None,
5722                files: USER,
5723            }),
5724            SettingsPageItem::SettingItem(SettingItem {
5725                title: "Agent Panel Max Content Width",
5726                description: "Maximum content width in pixels. Content will be centered when the panel is wider than this value.",
5727                field: Box::new(SettingField {
5728                    json_path: Some("agent.max_content_width"),
5729                    pick: |settings_content| {
5730                        settings_content.agent.as_ref()?.max_content_width.as_ref()
5731                    },
5732                    write: |settings_content, value| {
5733                        settings_content
5734                            .agent
5735                            .get_or_insert_default()
5736                            .max_content_width = value;
5737                    },
5738                }),
5739                metadata: None,
5740                files: USER,
5741            }),
5742        ]
5743    }
5744
5745    SettingsPage {
5746        title: "Panels",
5747        items: concat_sections![
5748            project_panel_section(),
5749            terminal_panel_section(),
5750            outline_panel_section(),
5751            git_panel_section(),
5752            debugger_panel_section(),
5753            collaboration_panel_section(),
5754            agent_panel_section(),
5755        ],
5756    }
5757}
5758
5759fn debugger_page() -> SettingsPage {
5760    fn general_section() -> [SettingsPageItem; 6] {
5761        [
5762            SettingsPageItem::SectionHeader("General"),
5763            SettingsPageItem::SettingItem(SettingItem {
5764                title: "Stepping Granularity",
5765                description: "Determines the stepping granularity for debug operations.",
5766                field: Box::new(SettingField {
5767                    json_path: Some("debugger.stepping_granularity"),
5768                    pick: |settings_content| {
5769                        settings_content
5770                            .debugger
5771                            .as_ref()?
5772                            .stepping_granularity
5773                            .as_ref()
5774                    },
5775                    write: |settings_content, value| {
5776                        settings_content
5777                            .debugger
5778                            .get_or_insert_default()
5779                            .stepping_granularity = value;
5780                    },
5781                }),
5782                metadata: None,
5783                files: USER,
5784            }),
5785            SettingsPageItem::SettingItem(SettingItem {
5786                title: "Save Breakpoints",
5787                description: "Whether breakpoints should be reused across Zed sessions.",
5788                field: Box::new(SettingField {
5789                    json_path: Some("debugger.save_breakpoints"),
5790                    pick: |settings_content| {
5791                        settings_content
5792                            .debugger
5793                            .as_ref()?
5794                            .save_breakpoints
5795                            .as_ref()
5796                    },
5797                    write: |settings_content, value| {
5798                        settings_content
5799                            .debugger
5800                            .get_or_insert_default()
5801                            .save_breakpoints = value;
5802                    },
5803                }),
5804                metadata: None,
5805                files: USER,
5806            }),
5807            SettingsPageItem::SettingItem(SettingItem {
5808                title: "Timeout",
5809                description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5810                field: Box::new(SettingField {
5811                    json_path: Some("debugger.timeout"),
5812                    pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5813                    write: |settings_content, value| {
5814                        settings_content.debugger.get_or_insert_default().timeout = value;
5815                    },
5816                }),
5817                metadata: None,
5818                files: USER,
5819            }),
5820            SettingsPageItem::SettingItem(SettingItem {
5821                title: "Log DAP Communications",
5822                description: "Whether to log messages between active debug adapters and Zed.",
5823                field: Box::new(SettingField {
5824                    json_path: Some("debugger.log_dap_communications"),
5825                    pick: |settings_content| {
5826                        settings_content
5827                            .debugger
5828                            .as_ref()?
5829                            .log_dap_communications
5830                            .as_ref()
5831                    },
5832                    write: |settings_content, value| {
5833                        settings_content
5834                            .debugger
5835                            .get_or_insert_default()
5836                            .log_dap_communications = value;
5837                    },
5838                }),
5839                metadata: None,
5840                files: USER,
5841            }),
5842            SettingsPageItem::SettingItem(SettingItem {
5843                title: "Format DAP Log Messages",
5844                description: "Whether to format DAP messages when adding them to debug adapter logger.",
5845                field: Box::new(SettingField {
5846                    json_path: Some("debugger.format_dap_log_messages"),
5847                    pick: |settings_content| {
5848                        settings_content
5849                            .debugger
5850                            .as_ref()?
5851                            .format_dap_log_messages
5852                            .as_ref()
5853                    },
5854                    write: |settings_content, value| {
5855                        settings_content
5856                            .debugger
5857                            .get_or_insert_default()
5858                            .format_dap_log_messages = value;
5859                    },
5860                }),
5861                metadata: None,
5862                files: USER,
5863            }),
5864        ]
5865    }
5866
5867    SettingsPage {
5868        title: "Debugger",
5869        items: concat_sections![general_section()],
5870    }
5871}
5872
5873fn terminal_page() -> SettingsPage {
5874    fn environment_section() -> [SettingsPageItem; 5] {
5875        [
5876                SettingsPageItem::SectionHeader("Environment"),
5877                SettingsPageItem::DynamicItem(DynamicItem {
5878                    discriminant: SettingItem {
5879                        files: USER | PROJECT,
5880                        title: "Shell",
5881                        description: "What shell to use when opening a terminal.",
5882                        field: Box::new(SettingField {
5883                            json_path: Some("terminal.shell$"),
5884                            pick: |settings_content| {
5885                                Some(&dynamic_variants::<settings::Shell>()[
5886                                    settings_content
5887                                        .terminal
5888                                        .as_ref()?
5889                                        .project
5890                                        .shell
5891                                        .as_ref()?
5892                                        .discriminant() as usize
5893                                ])
5894                            },
5895                            write: |settings_content, value| {
5896                                let Some(value) = value else {
5897                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5898                                        terminal.project.shell = None;
5899                                    }
5900                                    return;
5901                                };
5902                                let settings_value = settings_content
5903                                    .terminal
5904                                    .get_or_insert_default()
5905                                    .project
5906                                    .shell
5907                                    .get_or_insert_with(|| settings::Shell::default());
5908                                let default_shell = if cfg!(target_os = "windows") {
5909                                    "powershell.exe"
5910                                } else {
5911                                    "sh"
5912                                };
5913                                *settings_value = match value {
5914                                    settings::ShellDiscriminants::System => settings::Shell::System,
5915                                    settings::ShellDiscriminants::Program => {
5916                                        let program = match settings_value {
5917                                            settings::Shell::Program(program) => program.clone(),
5918                                            settings::Shell::WithArguments { program, .. } => program.clone(),
5919                                            _ => String::from(default_shell),
5920                                        };
5921                                        settings::Shell::Program(program)
5922                                    }
5923                                    settings::ShellDiscriminants::WithArguments => {
5924                                        let (program, args, title_override) = match settings_value {
5925                                            settings::Shell::Program(program) => (program.clone(), vec![], None),
5926                                            settings::Shell::WithArguments {
5927                                                program,
5928                                                args,
5929                                                title_override,
5930                                            } => (program.clone(), args.clone(), title_override.clone()),
5931                                            _ => (String::from(default_shell), vec![], None),
5932                                        };
5933                                        settings::Shell::WithArguments {
5934                                            program,
5935                                            args,
5936                                            title_override,
5937                                        }
5938                                    }
5939                                };
5940                            },
5941                        }),
5942                        metadata: None,
5943                    },
5944                    pick_discriminant: |settings_content| {
5945                        Some(
5946                            settings_content
5947                                .terminal
5948                                .as_ref()?
5949                                .project
5950                                .shell
5951                                .as_ref()?
5952                                .discriminant() as usize,
5953                        )
5954                    },
5955                    fields: dynamic_variants::<settings::Shell>()
5956                        .into_iter()
5957                        .map(|variant| match variant {
5958                            settings::ShellDiscriminants::System => vec![],
5959                            settings::ShellDiscriminants::Program => vec![SettingItem {
5960                                files: USER | PROJECT,
5961                                title: "Program",
5962                                description: "The shell program to use.",
5963                                field: Box::new(SettingField {
5964                                    json_path: Some("terminal.shell"),
5965                                    pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5966                                    {
5967                                        Some(settings::Shell::Program(program)) => Some(program),
5968                                        _ => None,
5969                                    },
5970                                    write: |settings_content, value| {
5971                                        let Some(value) = value else {
5972                                            return;
5973                                        };
5974                                        match settings_content
5975                                            .terminal
5976                                            .get_or_insert_default()
5977                                            .project
5978                                            .shell
5979                                            .as_mut()
5980                                        {
5981                                            Some(settings::Shell::Program(program)) => *program = value,
5982                                            _ => return,
5983                                        }
5984                                    },
5985                                }),
5986                                metadata: None,
5987                            }],
5988                            settings::ShellDiscriminants::WithArguments => vec![
5989                                SettingItem {
5990                                    files: USER | PROJECT,
5991                                    title: "Program",
5992                                    description: "The shell program to run.",
5993                                    field: Box::new(SettingField {
5994                                        json_path: Some("terminal.shell.program"),
5995                                        pick: |settings_content| {
5996                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5997                                                Some(settings::Shell::WithArguments { program, .. }) => Some(program),
5998                                                _ => None,
5999                                            }
6000                                        },
6001                                        write: |settings_content, value| {
6002                                            let Some(value) = value else {
6003                                                return;
6004                                            };
6005                                            match settings_content
6006                                                .terminal
6007                                                .get_or_insert_default()
6008                                                .project
6009                                                .shell
6010                                                .as_mut()
6011                                            {
6012                                                Some(settings::Shell::WithArguments { program, .. }) => {
6013                                                    *program = value
6014                                                }
6015                                                _ => return,
6016                                            }
6017                                        },
6018                                    }),
6019                                    metadata: None,
6020                                },
6021                                SettingItem {
6022                                    files: USER | PROJECT,
6023                                    title: "Arguments",
6024                                    description: "The arguments to pass to the shell program.",
6025                                    field: Box::new(
6026                                        SettingField {
6027                                            json_path: Some("terminal.shell.args"),
6028                                            pick: |settings_content| {
6029                                                match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6030                                                    Some(settings::Shell::WithArguments { args, .. }) => Some(args),
6031                                                    _ => None,
6032                                                }
6033                                            },
6034                                            write: |settings_content, value| {
6035                                                let Some(value) = value else {
6036                                                    return;
6037                                                };
6038                                                match settings_content
6039                                                    .terminal
6040                                                    .get_or_insert_default()
6041                                                    .project
6042                                                    .shell
6043                                                    .as_mut()
6044                                                {
6045                                                    Some(settings::Shell::WithArguments { args, .. }) => *args = value,
6046                                                    _ => return,
6047                                                }
6048                                            },
6049                                        }
6050                                        .unimplemented(),
6051                                    ),
6052                                    metadata: None,
6053                                },
6054                                SettingItem {
6055                                    files: USER | PROJECT,
6056                                    title: "Title Override",
6057                                    description: "An optional string to override the title of the terminal tab.",
6058                                    field: Box::new(SettingField {
6059                                        json_path: Some("terminal.shell.title_override"),
6060                                        pick: |settings_content| {
6061                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6062                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
6063                                                    title_override.as_ref().or(DEFAULT_EMPTY_STRING)
6064                                                }
6065                                                _ => None,
6066                                            }
6067                                        },
6068                                        write: |settings_content, value| {
6069                                            match settings_content
6070                                                .terminal
6071                                                .get_or_insert_default()
6072                                                .project
6073                                                .shell
6074                                                .as_mut()
6075                                            {
6076                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
6077                                                    *title_override = value.filter(|s| !s.is_empty())
6078                                                }
6079                                                _ => return,
6080                                            }
6081                                        },
6082                                    }),
6083                                    metadata: None,
6084                                },
6085                            ],
6086                        })
6087                        .collect(),
6088                }),
6089                SettingsPageItem::DynamicItem(DynamicItem {
6090                    discriminant: SettingItem {
6091                        files: USER | PROJECT,
6092                        title: "Working Directory",
6093                        description: "What working directory to use when launching the terminal.",
6094                        field: Box::new(SettingField {
6095                            json_path: Some("terminal.working_directory$"),
6096                            pick: |settings_content| {
6097                                Some(&dynamic_variants::<settings::WorkingDirectory>()[
6098                                    settings_content
6099                                        .terminal
6100                                        .as_ref()?
6101                                        .project
6102                                        .working_directory
6103                                        .as_ref()?
6104                                        .discriminant() as usize
6105                                ])
6106                            },
6107                            write: |settings_content, value| {
6108                                let Some(value) = value else {
6109                                    if let Some(terminal) = settings_content.terminal.as_mut() {
6110                                        terminal.project.working_directory = None;
6111                                    }
6112                                    return;
6113                                };
6114                                let settings_value = settings_content
6115                                    .terminal
6116                                    .get_or_insert_default()
6117                                    .project
6118                                    .working_directory
6119                                    .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
6120                                *settings_value = match value {
6121                                    settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
6122                                        settings::WorkingDirectory::CurrentFileDirectory
6123                                    },
6124                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
6125                                        settings::WorkingDirectory::CurrentProjectDirectory
6126                                    }
6127                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
6128                                        settings::WorkingDirectory::FirstProjectDirectory
6129                                    }
6130                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
6131                                        settings::WorkingDirectory::AlwaysHome
6132                                    }
6133                                    settings::WorkingDirectoryDiscriminants::Always => {
6134                                        let directory = match settings_value {
6135                                            settings::WorkingDirectory::Always { .. } => return,
6136                                            _ => String::new(),
6137                                        };
6138                                        settings::WorkingDirectory::Always { directory }
6139                                    }
6140                                };
6141                            },
6142                        }),
6143                        metadata: None,
6144                    },
6145                    pick_discriminant: |settings_content| {
6146                        Some(
6147                            settings_content
6148                                .terminal
6149                                .as_ref()?
6150                                .project
6151                                .working_directory
6152                                .as_ref()?
6153                                .discriminant() as usize,
6154                        )
6155                    },
6156                    fields: dynamic_variants::<settings::WorkingDirectory>()
6157                        .into_iter()
6158                        .map(|variant| match variant {
6159                            settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
6160                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
6161                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
6162                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
6163                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
6164                                files: USER | PROJECT,
6165                                title: "Directory",
6166                                description: "The directory path to use (will be shell expanded).",
6167                                field: Box::new(SettingField {
6168                                    json_path: Some("terminal.working_directory.always"),
6169                                    pick: |settings_content| {
6170                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
6171                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
6172                                            _ => None,
6173                                        }
6174                                    },
6175                                    write: |settings_content, value| {
6176                                        let value = value.unwrap_or_default();
6177                                        match settings_content
6178                                            .terminal
6179                                            .get_or_insert_default()
6180                                            .project
6181                                            .working_directory
6182                                            .as_mut()
6183                                        {
6184                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
6185                                            _ => return,
6186                                        }
6187                                    },
6188                                }),
6189                                metadata: None,
6190                            }],
6191                        })
6192                        .collect(),
6193                }),
6194                SettingsPageItem::SettingItem(SettingItem {
6195                    title: "Environment Variables",
6196                    description: "Key-value pairs to add to the terminal's environment.",
6197                    field: Box::new(
6198                        SettingField {
6199                            json_path: Some("terminal.env"),
6200                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
6201                            write: |settings_content, value| {
6202                                settings_content.terminal.get_or_insert_default().project.env = value;
6203                            },
6204                        }
6205                        .unimplemented(),
6206                    ),
6207                    metadata: None,
6208                    files: USER | PROJECT,
6209                }),
6210                SettingsPageItem::SettingItem(SettingItem {
6211                    title: "Detect Virtual Environment",
6212                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
6213                    field: Box::new(
6214                        SettingField {
6215                            json_path: Some("terminal.detect_venv"),
6216                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
6217                            write: |settings_content, value| {
6218                                settings_content
6219                                    .terminal
6220                                    .get_or_insert_default()
6221                                    .project
6222                                    .detect_venv = value;
6223                            },
6224                        }
6225                        .unimplemented(),
6226                    ),
6227                    metadata: None,
6228                    files: USER | PROJECT,
6229                }),
6230            ]
6231    }
6232
6233    fn font_section() -> [SettingsPageItem; 6] {
6234        [
6235            SettingsPageItem::SectionHeader("Font"),
6236            SettingsPageItem::SettingItem(SettingItem {
6237                title: "Font Size",
6238                description: "Font size for terminal text. If not set, defaults to buffer font size.",
6239                field: Box::new(SettingField {
6240                    json_path: Some("terminal.font_size"),
6241                    pick: |settings_content| {
6242                        settings_content
6243                            .terminal
6244                            .as_ref()
6245                            .and_then(|terminal| terminal.font_size.as_ref())
6246                            .or(settings_content.theme.buffer_font_size.as_ref())
6247                    },
6248                    write: |settings_content, value| {
6249                        settings_content.terminal.get_or_insert_default().font_size = value;
6250                    },
6251                }),
6252                metadata: None,
6253                files: USER,
6254            }),
6255            SettingsPageItem::SettingItem(SettingItem {
6256                title: "Font Family",
6257                description: "Font family for terminal text. If not set, defaults to buffer font family.",
6258                field: Box::new(SettingField {
6259                    json_path: Some("terminal.font_family"),
6260                    pick: |settings_content| {
6261                        settings_content
6262                            .terminal
6263                            .as_ref()
6264                            .and_then(|terminal| terminal.font_family.as_ref())
6265                            .or(settings_content.theme.buffer_font_family.as_ref())
6266                    },
6267                    write: |settings_content, value| {
6268                        settings_content
6269                            .terminal
6270                            .get_or_insert_default()
6271                            .font_family = value;
6272                    },
6273                }),
6274                metadata: None,
6275                files: USER,
6276            }),
6277            SettingsPageItem::SettingItem(SettingItem {
6278                title: "Font Fallbacks",
6279                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
6280                field: Box::new(
6281                    SettingField {
6282                        json_path: Some("terminal.font_fallbacks"),
6283                        pick: |settings_content| {
6284                            settings_content
6285                                .terminal
6286                                .as_ref()
6287                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
6288                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
6289                        },
6290                        write: |settings_content, value| {
6291                            settings_content
6292                                .terminal
6293                                .get_or_insert_default()
6294                                .font_fallbacks = value;
6295                        },
6296                    }
6297                    .unimplemented(),
6298                ),
6299                metadata: None,
6300                files: USER,
6301            }),
6302            SettingsPageItem::SettingItem(SettingItem {
6303                title: "Font Weight",
6304                description: "Font weight for terminal text in CSS weight units (100-900).",
6305                field: Box::new(SettingField {
6306                    json_path: Some("terminal.font_weight"),
6307                    pick: |settings_content| {
6308                        settings_content.terminal.as_ref()?.font_weight.as_ref()
6309                    },
6310                    write: |settings_content, value| {
6311                        settings_content
6312                            .terminal
6313                            .get_or_insert_default()
6314                            .font_weight = value;
6315                    },
6316                }),
6317                metadata: None,
6318                files: USER,
6319            }),
6320            SettingsPageItem::SettingItem(SettingItem {
6321                title: "Font Features",
6322                description: "Font features for terminal text.",
6323                field: Box::new(
6324                    SettingField {
6325                        json_path: Some("terminal.font_features"),
6326                        pick: |settings_content| {
6327                            settings_content
6328                                .terminal
6329                                .as_ref()
6330                                .and_then(|terminal| terminal.font_features.as_ref())
6331                                .or(settings_content.theme.buffer_font_features.as_ref())
6332                        },
6333                        write: |settings_content, value| {
6334                            settings_content
6335                                .terminal
6336                                .get_or_insert_default()
6337                                .font_features = value;
6338                        },
6339                    }
6340                    .unimplemented(),
6341                ),
6342                metadata: None,
6343                files: USER,
6344            }),
6345        ]
6346    }
6347
6348    fn display_settings_section() -> [SettingsPageItem; 6] {
6349        [
6350            SettingsPageItem::SectionHeader("Display Settings"),
6351            SettingsPageItem::SettingItem(SettingItem {
6352                title: "Line Height",
6353                description: "Line height for terminal text.",
6354                field: Box::new(
6355                    SettingField {
6356                        json_path: Some("terminal.line_height"),
6357                        pick: |settings_content| {
6358                            settings_content.terminal.as_ref()?.line_height.as_ref()
6359                        },
6360                        write: |settings_content, value| {
6361                            settings_content
6362                                .terminal
6363                                .get_or_insert_default()
6364                                .line_height = value;
6365                        },
6366                    }
6367                    .unimplemented(),
6368                ),
6369                metadata: None,
6370                files: USER,
6371            }),
6372            SettingsPageItem::SettingItem(SettingItem {
6373                title: "Cursor Shape",
6374                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6375                field: Box::new(SettingField {
6376                    json_path: Some("terminal.cursor_shape"),
6377                    pick: |settings_content| {
6378                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6379                    },
6380                    write: |settings_content, value| {
6381                        settings_content
6382                            .terminal
6383                            .get_or_insert_default()
6384                            .cursor_shape = value;
6385                    },
6386                }),
6387                metadata: None,
6388                files: USER,
6389            }),
6390            SettingsPageItem::SettingItem(SettingItem {
6391                title: "Cursor Blinking",
6392                description: "Sets the cursor blinking behavior in the terminal.",
6393                field: Box::new(SettingField {
6394                    json_path: Some("terminal.blinking"),
6395                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6396                    write: |settings_content, value| {
6397                        settings_content.terminal.get_or_insert_default().blinking = value;
6398                    },
6399                }),
6400                metadata: None,
6401                files: USER,
6402            }),
6403            SettingsPageItem::SettingItem(SettingItem {
6404                title: "Alternate Scroll",
6405                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6406                field: Box::new(SettingField {
6407                    json_path: Some("terminal.alternate_scroll"),
6408                    pick: |settings_content| {
6409                        settings_content
6410                            .terminal
6411                            .as_ref()?
6412                            .alternate_scroll
6413                            .as_ref()
6414                    },
6415                    write: |settings_content, value| {
6416                        settings_content
6417                            .terminal
6418                            .get_or_insert_default()
6419                            .alternate_scroll = value;
6420                    },
6421                }),
6422                metadata: None,
6423                files: USER,
6424            }),
6425            SettingsPageItem::SettingItem(SettingItem {
6426                title: "Minimum Contrast",
6427                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6428                field: Box::new(SettingField {
6429                    json_path: Some("terminal.minimum_contrast"),
6430                    pick: |settings_content| {
6431                        settings_content
6432                            .terminal
6433                            .as_ref()?
6434                            .minimum_contrast
6435                            .as_ref()
6436                    },
6437                    write: |settings_content, value| {
6438                        settings_content
6439                            .terminal
6440                            .get_or_insert_default()
6441                            .minimum_contrast = value;
6442                    },
6443                }),
6444                metadata: None,
6445                files: USER,
6446            }),
6447        ]
6448    }
6449
6450    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6451        [
6452            SettingsPageItem::SectionHeader("Behavior Settings"),
6453            SettingsPageItem::SettingItem(SettingItem {
6454                title: "Option As Meta",
6455                description: "Whether the option key behaves as the meta key.",
6456                field: Box::new(SettingField {
6457                    json_path: Some("terminal.option_as_meta"),
6458                    pick: |settings_content| {
6459                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6460                    },
6461                    write: |settings_content, value| {
6462                        settings_content
6463                            .terminal
6464                            .get_or_insert_default()
6465                            .option_as_meta = value;
6466                    },
6467                }),
6468                metadata: None,
6469                files: USER,
6470            }),
6471            SettingsPageItem::SettingItem(SettingItem {
6472                title: "Copy On Select",
6473                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6474                field: Box::new(SettingField {
6475                    json_path: Some("terminal.copy_on_select"),
6476                    pick: |settings_content| {
6477                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6478                    },
6479                    write: |settings_content, value| {
6480                        settings_content
6481                            .terminal
6482                            .get_or_insert_default()
6483                            .copy_on_select = value;
6484                    },
6485                }),
6486                metadata: None,
6487                files: USER,
6488            }),
6489            SettingsPageItem::SettingItem(SettingItem {
6490                title: "Keep Selection On Copy",
6491                description: "Whether to keep the text selection after copying it to the clipboard.",
6492                field: Box::new(SettingField {
6493                    json_path: Some("terminal.keep_selection_on_copy"),
6494                    pick: |settings_content| {
6495                        settings_content
6496                            .terminal
6497                            .as_ref()?
6498                            .keep_selection_on_copy
6499                            .as_ref()
6500                    },
6501                    write: |settings_content, value| {
6502                        settings_content
6503                            .terminal
6504                            .get_or_insert_default()
6505                            .keep_selection_on_copy = value;
6506                    },
6507                }),
6508                metadata: None,
6509                files: USER,
6510            }),
6511        ]
6512    }
6513
6514    fn layout_settings_section() -> [SettingsPageItem; 3] {
6515        [
6516            SettingsPageItem::SectionHeader("Layout Settings"),
6517            SettingsPageItem::SettingItem(SettingItem {
6518                title: "Default Width",
6519                description: "Default width when the terminal is docked to the left or right (in pixels).",
6520                field: Box::new(SettingField {
6521                    json_path: Some("terminal.default_width"),
6522                    pick: |settings_content| {
6523                        settings_content.terminal.as_ref()?.default_width.as_ref()
6524                    },
6525                    write: |settings_content, value| {
6526                        settings_content
6527                            .terminal
6528                            .get_or_insert_default()
6529                            .default_width = value;
6530                    },
6531                }),
6532                metadata: None,
6533                files: USER,
6534            }),
6535            SettingsPageItem::SettingItem(SettingItem {
6536                title: "Default Height",
6537                description: "Default height when the terminal is docked to the bottom (in pixels).",
6538                field: Box::new(SettingField {
6539                    json_path: Some("terminal.default_height"),
6540                    pick: |settings_content| {
6541                        settings_content.terminal.as_ref()?.default_height.as_ref()
6542                    },
6543                    write: |settings_content, value| {
6544                        settings_content
6545                            .terminal
6546                            .get_or_insert_default()
6547                            .default_height = value;
6548                    },
6549                }),
6550                metadata: None,
6551                files: USER,
6552            }),
6553        ]
6554    }
6555
6556    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6557        [
6558            SettingsPageItem::SectionHeader("Advanced Settings"),
6559            SettingsPageItem::SettingItem(SettingItem {
6560                title: "Max Scroll History Lines",
6561                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6562                field: Box::new(SettingField {
6563                    json_path: Some("terminal.max_scroll_history_lines"),
6564                    pick: |settings_content| {
6565                        settings_content
6566                            .terminal
6567                            .as_ref()?
6568                            .max_scroll_history_lines
6569                            .as_ref()
6570                    },
6571                    write: |settings_content, value| {
6572                        settings_content
6573                            .terminal
6574                            .get_or_insert_default()
6575                            .max_scroll_history_lines = value;
6576                    },
6577                }),
6578                metadata: None,
6579                files: USER,
6580            }),
6581            SettingsPageItem::SettingItem(SettingItem {
6582                title: "Scroll Multiplier",
6583                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6584                field: Box::new(SettingField {
6585                    json_path: Some("terminal.scroll_multiplier"),
6586                    pick: |settings_content| {
6587                        settings_content
6588                            .terminal
6589                            .as_ref()?
6590                            .scroll_multiplier
6591                            .as_ref()
6592                    },
6593                    write: |settings_content, value| {
6594                        settings_content
6595                            .terminal
6596                            .get_or_insert_default()
6597                            .scroll_multiplier = value;
6598                    },
6599                }),
6600                metadata: None,
6601                files: USER,
6602            }),
6603        ]
6604    }
6605
6606    fn toolbar_section() -> [SettingsPageItem; 2] {
6607        [
6608            SettingsPageItem::SectionHeader("Toolbar"),
6609            SettingsPageItem::SettingItem(SettingItem {
6610                title: "Breadcrumbs",
6611                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6612                field: Box::new(SettingField {
6613                    json_path: Some("terminal.toolbar.breadcrumbs"),
6614                    pick: |settings_content| {
6615                        settings_content
6616                            .terminal
6617                            .as_ref()?
6618                            .toolbar
6619                            .as_ref()?
6620                            .breadcrumbs
6621                            .as_ref()
6622                    },
6623                    write: |settings_content, value| {
6624                        settings_content
6625                            .terminal
6626                            .get_or_insert_default()
6627                            .toolbar
6628                            .get_or_insert_default()
6629                            .breadcrumbs = value;
6630                    },
6631                }),
6632                metadata: None,
6633                files: USER,
6634            }),
6635        ]
6636    }
6637
6638    fn scrollbar_section() -> [SettingsPageItem; 2] {
6639        [
6640            SettingsPageItem::SectionHeader("Scrollbar"),
6641            SettingsPageItem::SettingItem(SettingItem {
6642                title: "Show Scrollbar",
6643                description: "When to show the scrollbar in the terminal.",
6644                field: Box::new(SettingField {
6645                    json_path: Some("terminal.scrollbar.show"),
6646                    pick: |settings_content| {
6647                        show_scrollbar_or_editor(settings_content, |settings_content| {
6648                            settings_content
6649                                .terminal
6650                                .as_ref()?
6651                                .scrollbar
6652                                .as_ref()?
6653                                .show
6654                                .as_ref()
6655                        })
6656                    },
6657                    write: |settings_content, value| {
6658                        settings_content
6659                            .terminal
6660                            .get_or_insert_default()
6661                            .scrollbar
6662                            .get_or_insert_default()
6663                            .show = value;
6664                    },
6665                }),
6666                metadata: None,
6667                files: USER,
6668            }),
6669        ]
6670    }
6671
6672    SettingsPage {
6673        title: "Terminal",
6674        items: concat_sections![
6675            environment_section(),
6676            font_section(),
6677            display_settings_section(),
6678            behavior_settings_section(),
6679            layout_settings_section(),
6680            advanced_settings_section(),
6681            toolbar_section(),
6682            scrollbar_section(),
6683        ],
6684    }
6685}
6686
6687fn version_control_page() -> SettingsPage {
6688    fn git_integration_section() -> [SettingsPageItem; 2] {
6689        [
6690            SettingsPageItem::SectionHeader("Git Integration"),
6691            SettingsPageItem::DynamicItem(DynamicItem {
6692                discriminant: SettingItem {
6693                    files: USER,
6694                    title: "Disable Git Integration",
6695                    description: "Disable all Git integration features in Zed.",
6696                    field: Box::new(SettingField::<bool> {
6697                        json_path: Some("git.disable_git"),
6698                        pick: |settings_content| {
6699                            settings_content
6700                                .git
6701                                .as_ref()?
6702                                .enabled
6703                                .as_ref()?
6704                                .disable_git
6705                                .as_ref()
6706                        },
6707                        write: |settings_content, value| {
6708                            settings_content
6709                                .git
6710                                .get_or_insert_default()
6711                                .enabled
6712                                .get_or_insert_default()
6713                                .disable_git = value;
6714                        },
6715                    }),
6716                    metadata: None,
6717                },
6718                pick_discriminant: |settings_content| {
6719                    let disabled = settings_content
6720                        .git
6721                        .as_ref()?
6722                        .enabled
6723                        .as_ref()?
6724                        .disable_git
6725                        .unwrap_or(false);
6726                    Some(if disabled { 0 } else { 1 })
6727                },
6728                fields: vec![
6729                    vec![],
6730                    vec![
6731                        SettingItem {
6732                            files: USER,
6733                            title: "Enable Git Status",
6734                            description: "Show Git status information in the editor.",
6735                            field: Box::new(SettingField::<bool> {
6736                                json_path: Some("git.enable_status"),
6737                                pick: |settings_content| {
6738                                    settings_content
6739                                        .git
6740                                        .as_ref()?
6741                                        .enabled
6742                                        .as_ref()?
6743                                        .enable_status
6744                                        .as_ref()
6745                                },
6746                                write: |settings_content, value| {
6747                                    settings_content
6748                                        .git
6749                                        .get_or_insert_default()
6750                                        .enabled
6751                                        .get_or_insert_default()
6752                                        .enable_status = value;
6753                                },
6754                            }),
6755                            metadata: None,
6756                        },
6757                        SettingItem {
6758                            files: USER,
6759                            title: "Enable Git Diff",
6760                            description: "Show Git diff information in the editor.",
6761                            field: Box::new(SettingField::<bool> {
6762                                json_path: Some("git.enable_diff"),
6763                                pick: |settings_content| {
6764                                    settings_content
6765                                        .git
6766                                        .as_ref()?
6767                                        .enabled
6768                                        .as_ref()?
6769                                        .enable_diff
6770                                        .as_ref()
6771                                },
6772                                write: |settings_content, value| {
6773                                    settings_content
6774                                        .git
6775                                        .get_or_insert_default()
6776                                        .enabled
6777                                        .get_or_insert_default()
6778                                        .enable_diff = value;
6779                                },
6780                            }),
6781                            metadata: None,
6782                        },
6783                    ],
6784                ],
6785            }),
6786        ]
6787    }
6788
6789    fn git_gutter_section() -> [SettingsPageItem; 3] {
6790        [
6791            SettingsPageItem::SectionHeader("Git Gutter"),
6792            SettingsPageItem::SettingItem(SettingItem {
6793                title: "Visibility",
6794                description: "Control whether Git status is shown in the editor's gutter.",
6795                field: Box::new(SettingField {
6796                    json_path: Some("git.git_gutter"),
6797                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6798                    write: |settings_content, value| {
6799                        settings_content.git.get_or_insert_default().git_gutter = value;
6800                    },
6801                }),
6802                metadata: None,
6803                files: USER,
6804            }),
6805            // todo(settings_ui): Figure out the right default for this value in default.json
6806            SettingsPageItem::SettingItem(SettingItem {
6807                title: "Debounce",
6808                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6809                field: Box::new(SettingField {
6810                    json_path: Some("git.gutter_debounce"),
6811                    pick: |settings_content| {
6812                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6813                    },
6814                    write: |settings_content, value| {
6815                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6816                    },
6817                }),
6818                metadata: None,
6819                files: USER,
6820            }),
6821        ]
6822    }
6823
6824    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6825        [
6826            SettingsPageItem::SectionHeader("Inline Git Blame"),
6827            SettingsPageItem::SettingItem(SettingItem {
6828                title: "Enabled",
6829                description: "Whether or not to show Git blame data inline in the currently focused line.",
6830                field: Box::new(SettingField {
6831                    json_path: Some("git.inline_blame.enabled"),
6832                    pick: |settings_content| {
6833                        settings_content
6834                            .git
6835                            .as_ref()?
6836                            .inline_blame
6837                            .as_ref()?
6838                            .enabled
6839                            .as_ref()
6840                    },
6841                    write: |settings_content, value| {
6842                        settings_content
6843                            .git
6844                            .get_or_insert_default()
6845                            .inline_blame
6846                            .get_or_insert_default()
6847                            .enabled = value;
6848                    },
6849                }),
6850                metadata: None,
6851                files: USER,
6852            }),
6853            SettingsPageItem::SettingItem(SettingItem {
6854                title: "Delay",
6855                description: "The delay after which the inline blame information is shown.",
6856                field: Box::new(SettingField {
6857                    json_path: Some("git.inline_blame.delay_ms"),
6858                    pick: |settings_content| {
6859                        settings_content
6860                            .git
6861                            .as_ref()?
6862                            .inline_blame
6863                            .as_ref()?
6864                            .delay_ms
6865                            .as_ref()
6866                    },
6867                    write: |settings_content, value| {
6868                        settings_content
6869                            .git
6870                            .get_or_insert_default()
6871                            .inline_blame
6872                            .get_or_insert_default()
6873                            .delay_ms = value;
6874                    },
6875                }),
6876                metadata: None,
6877                files: USER,
6878            }),
6879            SettingsPageItem::SettingItem(SettingItem {
6880                title: "Padding",
6881                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6882                field: Box::new(SettingField {
6883                    json_path: Some("git.inline_blame.padding"),
6884                    pick: |settings_content| {
6885                        settings_content
6886                            .git
6887                            .as_ref()?
6888                            .inline_blame
6889                            .as_ref()?
6890                            .padding
6891                            .as_ref()
6892                    },
6893                    write: |settings_content, value| {
6894                        settings_content
6895                            .git
6896                            .get_or_insert_default()
6897                            .inline_blame
6898                            .get_or_insert_default()
6899                            .padding = value;
6900                    },
6901                }),
6902                metadata: None,
6903                files: USER,
6904            }),
6905            SettingsPageItem::SettingItem(SettingItem {
6906                title: "Minimum Column",
6907                description: "The minimum column number at which to show the inline blame information.",
6908                field: Box::new(SettingField {
6909                    json_path: Some("git.inline_blame.min_column"),
6910                    pick: |settings_content| {
6911                        settings_content
6912                            .git
6913                            .as_ref()?
6914                            .inline_blame
6915                            .as_ref()?
6916                            .min_column
6917                            .as_ref()
6918                    },
6919                    write: |settings_content, value| {
6920                        settings_content
6921                            .git
6922                            .get_or_insert_default()
6923                            .inline_blame
6924                            .get_or_insert_default()
6925                            .min_column = value;
6926                    },
6927                }),
6928                metadata: None,
6929                files: USER,
6930            }),
6931            SettingsPageItem::SettingItem(SettingItem {
6932                title: "Show Commit Summary",
6933                description: "Show commit summary as part of the inline blame.",
6934                field: Box::new(SettingField {
6935                    json_path: Some("git.inline_blame.show_commit_summary"),
6936                    pick: |settings_content| {
6937                        settings_content
6938                            .git
6939                            .as_ref()?
6940                            .inline_blame
6941                            .as_ref()?
6942                            .show_commit_summary
6943                            .as_ref()
6944                    },
6945                    write: |settings_content, value| {
6946                        settings_content
6947                            .git
6948                            .get_or_insert_default()
6949                            .inline_blame
6950                            .get_or_insert_default()
6951                            .show_commit_summary = value;
6952                    },
6953                }),
6954                metadata: None,
6955                files: USER,
6956            }),
6957        ]
6958    }
6959
6960    fn git_blame_view_section() -> [SettingsPageItem; 2] {
6961        [
6962            SettingsPageItem::SectionHeader("Git Blame View"),
6963            SettingsPageItem::SettingItem(SettingItem {
6964                title: "Show Avatar",
6965                description: "Show the avatar of the author of the commit.",
6966                field: Box::new(SettingField {
6967                    json_path: Some("git.blame.show_avatar"),
6968                    pick: |settings_content| {
6969                        settings_content
6970                            .git
6971                            .as_ref()?
6972                            .blame
6973                            .as_ref()?
6974                            .show_avatar
6975                            .as_ref()
6976                    },
6977                    write: |settings_content, value| {
6978                        settings_content
6979                            .git
6980                            .get_or_insert_default()
6981                            .blame
6982                            .get_or_insert_default()
6983                            .show_avatar = value;
6984                    },
6985                }),
6986                metadata: None,
6987                files: USER,
6988            }),
6989        ]
6990    }
6991
6992    fn branch_picker_section() -> [SettingsPageItem; 2] {
6993        [
6994            SettingsPageItem::SectionHeader("Branch Picker"),
6995            SettingsPageItem::SettingItem(SettingItem {
6996                title: "Show Author Name",
6997                description: "Show author name as part of the commit information in branch picker.",
6998                field: Box::new(SettingField {
6999                    json_path: Some("git.branch_picker.show_author_name"),
7000                    pick: |settings_content| {
7001                        settings_content
7002                            .git
7003                            .as_ref()?
7004                            .branch_picker
7005                            .as_ref()?
7006                            .show_author_name
7007                            .as_ref()
7008                    },
7009                    write: |settings_content, value| {
7010                        settings_content
7011                            .git
7012                            .get_or_insert_default()
7013                            .branch_picker
7014                            .get_or_insert_default()
7015                            .show_author_name = value;
7016                    },
7017                }),
7018                metadata: None,
7019                files: USER,
7020            }),
7021        ]
7022    }
7023
7024    fn git_hunks_section() -> [SettingsPageItem; 3] {
7025        [
7026            SettingsPageItem::SectionHeader("Git Hunks"),
7027            SettingsPageItem::SettingItem(SettingItem {
7028                title: "Hunk Style",
7029                description: "How Git hunks are displayed visually in the editor.",
7030                field: Box::new(SettingField {
7031                    json_path: Some("git.hunk_style"),
7032                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
7033                    write: |settings_content, value| {
7034                        settings_content.git.get_or_insert_default().hunk_style = value;
7035                    },
7036                }),
7037                metadata: None,
7038                files: USER,
7039            }),
7040            SettingsPageItem::SettingItem(SettingItem {
7041                title: "Path Style",
7042                description: "Should the name or path be displayed first in the git view.",
7043                field: Box::new(SettingField {
7044                    json_path: Some("git.path_style"),
7045                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
7046                    write: |settings_content, value| {
7047                        settings_content.git.get_or_insert_default().path_style = value;
7048                    },
7049                }),
7050                metadata: None,
7051                files: USER,
7052            }),
7053        ]
7054    }
7055
7056    SettingsPage {
7057        title: "Version Control",
7058        items: concat_sections![
7059            git_integration_section(),
7060            git_gutter_section(),
7061            inline_git_blame_section(),
7062            git_blame_view_section(),
7063            branch_picker_section(),
7064            git_hunks_section(),
7065        ],
7066    }
7067}
7068
7069fn collaboration_page() -> SettingsPage {
7070    fn calls_section() -> [SettingsPageItem; 3] {
7071        [
7072            SettingsPageItem::SectionHeader("Calls"),
7073            SettingsPageItem::SettingItem(SettingItem {
7074                title: "Mute On Join",
7075                description: "Whether the microphone should be muted when joining a channel or a call.",
7076                field: Box::new(SettingField {
7077                    json_path: Some("calls.mute_on_join"),
7078                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
7079                    write: |settings_content, value| {
7080                        settings_content.calls.get_or_insert_default().mute_on_join = value;
7081                    },
7082                }),
7083                metadata: None,
7084                files: USER,
7085            }),
7086            SettingsPageItem::SettingItem(SettingItem {
7087                title: "Share On Join",
7088                description: "Whether your current project should be shared when joining an empty channel.",
7089                field: Box::new(SettingField {
7090                    json_path: Some("calls.share_on_join"),
7091                    pick: |settings_content| {
7092                        settings_content.calls.as_ref()?.share_on_join.as_ref()
7093                    },
7094                    write: |settings_content, value| {
7095                        settings_content.calls.get_or_insert_default().share_on_join = value;
7096                    },
7097                }),
7098                metadata: None,
7099                files: USER,
7100            }),
7101        ]
7102    }
7103
7104    fn audio_settings() -> [SettingsPageItem; 3] {
7105        [
7106            SettingsPageItem::ActionLink(ActionLink {
7107                title: "Test Audio".into(),
7108                description: Some("Test your microphone and speaker setup".into()),
7109                button_text: "Test Audio".into(),
7110                on_click: Arc::new(|_settings_window, window, cx| {
7111                    open_audio_test_window(window, cx);
7112                }),
7113                files: USER,
7114            }),
7115            SettingsPageItem::SettingItem(SettingItem {
7116                title: "Output Audio Device",
7117                description: "Select output audio device",
7118                field: Box::new(SettingField {
7119                    json_path: Some("audio.experimental.output_audio_device"),
7120                    pick: |settings_content| {
7121                        settings_content
7122                            .audio
7123                            .as_ref()?
7124                            .output_audio_device
7125                            .as_ref()
7126                            .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
7127                    },
7128                    write: |settings_content, value| {
7129                        settings_content
7130                            .audio
7131                            .get_or_insert_default()
7132                            .output_audio_device = value;
7133                    },
7134                }),
7135                metadata: None,
7136                files: USER,
7137            }),
7138            SettingsPageItem::SettingItem(SettingItem {
7139                title: "Input Audio Device",
7140                description: "Select input audio device",
7141                field: Box::new(SettingField {
7142                    json_path: Some("audio.experimental.input_audio_device"),
7143                    pick: |settings_content| {
7144                        settings_content
7145                            .audio
7146                            .as_ref()?
7147                            .input_audio_device
7148                            .as_ref()
7149                            .or(DEFAULT_EMPTY_AUDIO_INPUT)
7150                    },
7151                    write: |settings_content, value| {
7152                        settings_content
7153                            .audio
7154                            .get_or_insert_default()
7155                            .input_audio_device = value;
7156                    },
7157                }),
7158                metadata: None,
7159                files: USER,
7160            }),
7161        ]
7162    }
7163
7164    SettingsPage {
7165        title: "Collaboration",
7166        items: concat_sections![calls_section(), audio_settings()],
7167    }
7168}
7169
7170fn ai_page(cx: &App) -> SettingsPage {
7171    fn general_section() -> [SettingsPageItem; 2] {
7172        [
7173            SettingsPageItem::SectionHeader("General"),
7174            SettingsPageItem::SettingItem(SettingItem {
7175                title: "Disable AI",
7176                description: "Whether to disable all AI features in Zed.",
7177                field: Box::new(SettingField {
7178                    json_path: Some("disable_ai"),
7179                    pick: |settings_content| settings_content.project.disable_ai.as_ref(),
7180                    write: |settings_content, value| {
7181                        settings_content.project.disable_ai = value;
7182                    },
7183                }),
7184                metadata: None,
7185                files: USER | PROJECT,
7186            }),
7187        ]
7188    }
7189
7190    fn agent_configuration_section(_cx: &App) -> Box<[SettingsPageItem]> {
7191        let mut items = vec![
7192            SettingsPageItem::SectionHeader("Agent Configuration"),
7193            SettingsPageItem::SubPageLink(SubPageLink {
7194                title: "Tool Permissions".into(),
7195                r#type: Default::default(),
7196                json_path: Some("agent.tool_permissions"),
7197                description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
7198                in_json: true,
7199                files: USER,
7200                render: render_tool_permissions_setup_page,
7201            }),
7202        ];
7203
7204        items.push(SettingsPageItem::SettingItem(SettingItem {
7205            title: "New Thread Location",
7206            description: "Whether to start a new thread in the current local project or in a new Git worktree.",
7207            field: Box::new(SettingField {
7208                json_path: Some("agent.new_thread_location"),
7209                pick: |settings_content| {
7210                    settings_content
7211                        .agent
7212                        .as_ref()?
7213                        .new_thread_location
7214                        .as_ref()
7215                },
7216                write: |settings_content, value| {
7217                    settings_content
7218                        .agent
7219                        .get_or_insert_default()
7220                        .new_thread_location = value;
7221                },
7222            }),
7223            metadata: None,
7224            files: USER,
7225        }));
7226
7227        items.extend([
7228            SettingsPageItem::SettingItem(SettingItem {
7229                title: "Single File Review",
7230                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
7231                field: Box::new(SettingField {
7232                    json_path: Some("agent.single_file_review"),
7233                    pick: |settings_content| {
7234                        settings_content.agent.as_ref()?.single_file_review.as_ref()
7235                    },
7236                    write: |settings_content, value| {
7237                        settings_content
7238                            .agent
7239                            .get_or_insert_default()
7240                            .single_file_review = value;
7241                    },
7242                }),
7243                metadata: None,
7244                files: USER,
7245            }),
7246            SettingsPageItem::SettingItem(SettingItem {
7247                title: "Enable Feedback",
7248                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
7249                field: Box::new(SettingField {
7250                    json_path: Some("agent.enable_feedback"),
7251                    pick: |settings_content| {
7252                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
7253                    },
7254                    write: |settings_content, value| {
7255                        settings_content
7256                            .agent
7257                            .get_or_insert_default()
7258                            .enable_feedback = value;
7259                    },
7260                }),
7261                metadata: None,
7262                files: USER,
7263            }),
7264            SettingsPageItem::SettingItem(SettingItem {
7265                title: "Notify When Agent Waiting",
7266                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7267                field: Box::new(SettingField {
7268                    json_path: Some("agent.notify_when_agent_waiting"),
7269                    pick: |settings_content| {
7270                        settings_content
7271                            .agent
7272                            .as_ref()?
7273                            .notify_when_agent_waiting
7274                            .as_ref()
7275                    },
7276                    write: |settings_content, value| {
7277                        settings_content
7278                            .agent
7279                            .get_or_insert_default()
7280                            .notify_when_agent_waiting = value;
7281                    },
7282                }),
7283                metadata: None,
7284                files: USER,
7285            }),
7286            SettingsPageItem::SettingItem(SettingItem {
7287                title: "Play Sound When Agent Done",
7288                description: "When to play a sound when the agent has either completed its response, or needs user input.",
7289                field: Box::new(SettingField {
7290                    json_path: Some("agent.play_sound_when_agent_done"),
7291                    pick: |settings_content| {
7292                        settings_content
7293                            .agent
7294                            .as_ref()?
7295                            .play_sound_when_agent_done
7296                            .as_ref()
7297                    },
7298                    write: |settings_content, value| {
7299                        settings_content
7300                            .agent
7301                            .get_or_insert_default()
7302                            .play_sound_when_agent_done = value;
7303                    },
7304                }),
7305                metadata: None,
7306                files: USER,
7307            }),
7308            SettingsPageItem::SettingItem(SettingItem {
7309                title: "Expand Edit Card",
7310                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7311                field: Box::new(SettingField {
7312                    json_path: Some("agent.expand_edit_card"),
7313                    pick: |settings_content| {
7314                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7315                    },
7316                    write: |settings_content, value| {
7317                        settings_content
7318                            .agent
7319                            .get_or_insert_default()
7320                            .expand_edit_card = value;
7321                    },
7322                }),
7323                metadata: None,
7324                files: USER,
7325            }),
7326            SettingsPageItem::SettingItem(SettingItem {
7327                title: "Expand Terminal Card",
7328                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7329                field: Box::new(SettingField {
7330                    json_path: Some("agent.expand_terminal_card"),
7331                    pick: |settings_content| {
7332                        settings_content
7333                            .agent
7334                            .as_ref()?
7335                            .expand_terminal_card
7336                            .as_ref()
7337                    },
7338                    write: |settings_content, value| {
7339                        settings_content
7340                            .agent
7341                            .get_or_insert_default()
7342                            .expand_terminal_card = value;
7343                    },
7344                }),
7345                metadata: None,
7346                files: USER,
7347            }),
7348            SettingsPageItem::SettingItem(SettingItem {
7349                title: "Thinking Display",
7350                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.",
7351                field: Box::new(SettingField {
7352                    json_path: Some("agent.thinking_display"),
7353                    pick: |settings_content| {
7354                        settings_content
7355                            .agent
7356                            .as_ref()?
7357                            .thinking_display
7358                            .as_ref()
7359                    },
7360                    write: |settings_content, value| {
7361                        settings_content
7362                            .agent
7363                            .get_or_insert_default()
7364                            .thinking_display = value;
7365                    },
7366                }),
7367                metadata: None,
7368                files: USER,
7369            }),
7370            SettingsPageItem::SettingItem(SettingItem {
7371                title: "Cancel Generation On Terminal Stop",
7372                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.",
7373                field: Box::new(SettingField {
7374                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
7375                    pick: |settings_content| {
7376                        settings_content
7377                            .agent
7378                            .as_ref()?
7379                            .cancel_generation_on_terminal_stop
7380                            .as_ref()
7381                    },
7382                    write: |settings_content, value| {
7383                        settings_content
7384                            .agent
7385                            .get_or_insert_default()
7386                            .cancel_generation_on_terminal_stop = value;
7387                    },
7388                }),
7389                metadata: None,
7390                files: USER,
7391            }),
7392            SettingsPageItem::SettingItem(SettingItem {
7393                title: "Use Modifier To Send",
7394                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7395                field: Box::new(SettingField {
7396                    json_path: Some("agent.use_modifier_to_send"),
7397                    pick: |settings_content| {
7398                        settings_content
7399                            .agent
7400                            .as_ref()?
7401                            .use_modifier_to_send
7402                            .as_ref()
7403                    },
7404                    write: |settings_content, value| {
7405                        settings_content
7406                            .agent
7407                            .get_or_insert_default()
7408                            .use_modifier_to_send = value;
7409                    },
7410                }),
7411                metadata: None,
7412                files: USER,
7413            }),
7414            SettingsPageItem::SettingItem(SettingItem {
7415                title: "Message Editor Min Lines",
7416                description: "Minimum number of lines to display in the agent message editor.",
7417                field: Box::new(SettingField {
7418                    json_path: Some("agent.message_editor_min_lines"),
7419                    pick: |settings_content| {
7420                        settings_content
7421                            .agent
7422                            .as_ref()?
7423                            .message_editor_min_lines
7424                            .as_ref()
7425                    },
7426                    write: |settings_content, value| {
7427                        settings_content
7428                            .agent
7429                            .get_or_insert_default()
7430                            .message_editor_min_lines = value;
7431                    },
7432                }),
7433                metadata: None,
7434                files: USER,
7435            }),
7436            SettingsPageItem::SettingItem(SettingItem {
7437                title: "Show Turn Stats",
7438                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7439                field: Box::new(SettingField {
7440                    json_path: Some("agent.show_turn_stats"),
7441                    pick: |settings_content| {
7442                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7443                    },
7444                    write: |settings_content, value| {
7445                        settings_content
7446                            .agent
7447                            .get_or_insert_default()
7448                            .show_turn_stats = value;
7449                    },
7450                }),
7451                metadata: None,
7452                files: USER,
7453            }),
7454            SettingsPageItem::SettingItem(SettingItem {
7455                title: "Show Merge Conflict Indicator",
7456                description: "Whether to show the merge conflict indicator in the status bar that offers to resolve conflicts using the agent.",
7457                field: Box::new(SettingField {
7458                    json_path: Some("agent.show_merge_conflict_indicator"),
7459                    pick: |settings_content| {
7460                        settings_content.agent.as_ref()?.show_merge_conflict_indicator.as_ref()
7461                    },
7462                    write: |settings_content, value| {
7463                        settings_content
7464                            .agent
7465                            .get_or_insert_default()
7466                            .show_merge_conflict_indicator = value;
7467                    },
7468                }),
7469                metadata: None,
7470                files: USER,
7471            }),
7472        ]);
7473
7474        items.into_boxed_slice()
7475    }
7476
7477    fn context_servers_section() -> [SettingsPageItem; 2] {
7478        [
7479            SettingsPageItem::SectionHeader("Context Servers"),
7480            SettingsPageItem::SettingItem(SettingItem {
7481                title: "Context Server Timeout",
7482                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7483                field: Box::new(SettingField {
7484                    json_path: Some("context_server_timeout"),
7485                    pick: |settings_content| {
7486                        settings_content.project.context_server_timeout.as_ref()
7487                    },
7488                    write: |settings_content, value| {
7489                        settings_content.project.context_server_timeout = value;
7490                    },
7491                }),
7492                metadata: None,
7493                files: USER | PROJECT,
7494            }),
7495        ]
7496    }
7497
7498    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 1] {
7499        [SettingsPageItem::SettingItem(SettingItem {
7500            title: "Display Mode",
7501            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.",
7502            field: Box::new(SettingField {
7503                json_path: Some("edit_prediction.display_mode"),
7504                pick: |settings_content| {
7505                    settings_content
7506                        .project
7507                        .all_languages
7508                        .edit_predictions
7509                        .as_ref()?
7510                        .mode
7511                        .as_ref()
7512                },
7513                write: |settings_content, value| {
7514                    settings_content
7515                        .project
7516                        .all_languages
7517                        .edit_predictions
7518                        .get_or_insert_default()
7519                        .mode = value;
7520                },
7521            }),
7522            metadata: None,
7523            files: USER,
7524        })]
7525    }
7526
7527    SettingsPage {
7528        title: "AI",
7529        items: concat_sections![
7530            general_section(),
7531            agent_configuration_section(cx),
7532            context_servers_section(),
7533            edit_prediction_language_settings_section(),
7534            edit_prediction_display_sub_section()
7535        ],
7536    }
7537}
7538
7539fn network_page() -> SettingsPage {
7540    fn network_section() -> [SettingsPageItem; 3] {
7541        [
7542            SettingsPageItem::SectionHeader("Network"),
7543            SettingsPageItem::SettingItem(SettingItem {
7544                title: "Proxy",
7545                description: "The proxy to use for network requests.",
7546                field: Box::new(SettingField {
7547                    json_path: Some("proxy"),
7548                    pick: |settings_content| settings_content.proxy.as_ref(),
7549                    write: |settings_content, value| {
7550                        settings_content.proxy = value;
7551                    },
7552                }),
7553                metadata: Some(Box::new(SettingsFieldMetadata {
7554                    placeholder: Some("socks5h://localhost:10808"),
7555                    ..Default::default()
7556                })),
7557                files: USER,
7558            }),
7559            SettingsPageItem::SettingItem(SettingItem {
7560                title: "Server URL",
7561                description: "The URL of the Zed server to connect to.",
7562                field: Box::new(SettingField {
7563                    json_path: Some("server_url"),
7564                    pick: |settings_content| settings_content.server_url.as_ref(),
7565                    write: |settings_content, value| {
7566                        settings_content.server_url = value;
7567                    },
7568                }),
7569                metadata: Some(Box::new(SettingsFieldMetadata {
7570                    placeholder: Some("https://zed.dev"),
7571                    ..Default::default()
7572                })),
7573                files: USER,
7574            }),
7575        ]
7576    }
7577
7578    SettingsPage {
7579        title: "Network",
7580        items: concat_sections![network_section()],
7581    }
7582}
7583
7584fn language_settings_field<T>(
7585    settings_content: &SettingsContent,
7586    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7587) -> Option<&T> {
7588    let all_languages = &settings_content.project.all_languages;
7589
7590    active_language()
7591        .and_then(|current_language_name| {
7592            all_languages
7593                .languages
7594                .0
7595                .get(current_language_name.as_ref())
7596        })
7597        .and_then(get_language_setting_field)
7598        .or_else(|| get_language_setting_field(&all_languages.defaults))
7599}
7600
7601fn language_settings_field_mut<T>(
7602    settings_content: &mut SettingsContent,
7603    value: Option<T>,
7604    write: fn(&mut LanguageSettingsContent, Option<T>),
7605) {
7606    let all_languages = &mut settings_content.project.all_languages;
7607    let language_content = if let Some(current_language) = active_language() {
7608        all_languages
7609            .languages
7610            .0
7611            .entry(current_language.to_string())
7612            .or_default()
7613    } else {
7614        &mut all_languages.defaults
7615    };
7616    write(language_content, value);
7617}
7618
7619fn language_settings_data() -> Box<[SettingsPageItem]> {
7620    fn indentation_section() -> [SettingsPageItem; 5] {
7621        [
7622            SettingsPageItem::SectionHeader("Indentation"),
7623            SettingsPageItem::SettingItem(SettingItem {
7624                title: "Tab Size",
7625                description: "How many columns a tab should occupy.",
7626                field: Box::new(SettingField {
7627                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7628                    pick: |settings_content| {
7629                        language_settings_field(settings_content, |language| {
7630                            language.tab_size.as_ref()
7631                        })
7632                    },
7633                    write: |settings_content, value| {
7634                        language_settings_field_mut(settings_content, value, |language, value| {
7635                            language.tab_size = value;
7636                        })
7637                    },
7638                }),
7639                metadata: None,
7640                files: USER | PROJECT,
7641            }),
7642            SettingsPageItem::SettingItem(SettingItem {
7643                title: "Hard Tabs",
7644                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7645                field: Box::new(SettingField {
7646                    json_path: Some("languages.$(language).hard_tabs"),
7647                    pick: |settings_content| {
7648                        language_settings_field(settings_content, |language| {
7649                            language.hard_tabs.as_ref()
7650                        })
7651                    },
7652                    write: |settings_content, value| {
7653                        language_settings_field_mut(settings_content, value, |language, value| {
7654                            language.hard_tabs = value;
7655                        })
7656                    },
7657                }),
7658                metadata: None,
7659                files: USER | PROJECT,
7660            }),
7661            SettingsPageItem::SettingItem(SettingItem {
7662                title: "Auto Indent",
7663                description: "Controls automatic indentation behavior when typing.",
7664                field: Box::new(SettingField {
7665                    json_path: Some("languages.$(language).auto_indent"),
7666                    pick: |settings_content| {
7667                        language_settings_field(settings_content, |language| {
7668                            language.auto_indent.as_ref()
7669                        })
7670                    },
7671                    write: |settings_content, value| {
7672                        language_settings_field_mut(settings_content, value, |language, value| {
7673                            language.auto_indent = value;
7674                        })
7675                    },
7676                }),
7677                metadata: None,
7678                files: USER | PROJECT,
7679            }),
7680            SettingsPageItem::SettingItem(SettingItem {
7681                title: "Auto Indent On Paste",
7682                description: "Whether indentation of pasted content should be adjusted based on the context.",
7683                field: Box::new(SettingField {
7684                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7685                    pick: |settings_content| {
7686                        language_settings_field(settings_content, |language| {
7687                            language.auto_indent_on_paste.as_ref()
7688                        })
7689                    },
7690                    write: |settings_content, value| {
7691                        language_settings_field_mut(settings_content, value, |language, value| {
7692                            language.auto_indent_on_paste = value;
7693                        })
7694                    },
7695                }),
7696                metadata: None,
7697                files: USER | PROJECT,
7698            }),
7699        ]
7700    }
7701
7702    fn wrapping_section() -> [SettingsPageItem; 6] {
7703        [
7704            SettingsPageItem::SectionHeader("Wrapping"),
7705            SettingsPageItem::SettingItem(SettingItem {
7706                title: "Soft Wrap",
7707                description: "How to soft-wrap long lines of text.",
7708                field: Box::new(SettingField {
7709                    json_path: Some("languages.$(language).soft_wrap"),
7710                    pick: |settings_content| {
7711                        language_settings_field(settings_content, |language| {
7712                            language.soft_wrap.as_ref()
7713                        })
7714                    },
7715                    write: |settings_content, value| {
7716                        language_settings_field_mut(settings_content, value, |language, value| {
7717                            language.soft_wrap = value;
7718                        })
7719                    },
7720                }),
7721                metadata: None,
7722                files: USER | PROJECT,
7723            }),
7724            SettingsPageItem::SettingItem(SettingItem {
7725                title: "Show Wrap Guides",
7726                description: "Show wrap guides in the editor.",
7727                field: Box::new(SettingField {
7728                    json_path: Some("languages.$(language).show_wrap_guides"),
7729                    pick: |settings_content| {
7730                        language_settings_field(settings_content, |language| {
7731                            language.show_wrap_guides.as_ref()
7732                        })
7733                    },
7734                    write: |settings_content, value| {
7735                        language_settings_field_mut(settings_content, value, |language, value| {
7736                            language.show_wrap_guides = value;
7737                        })
7738                    },
7739                }),
7740                metadata: None,
7741                files: USER | PROJECT,
7742            }),
7743            SettingsPageItem::SettingItem(SettingItem {
7744                title: "Preferred Line Length",
7745                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7746                field: Box::new(SettingField {
7747                    json_path: Some("languages.$(language).preferred_line_length"),
7748                    pick: |settings_content| {
7749                        language_settings_field(settings_content, |language| {
7750                            language.preferred_line_length.as_ref()
7751                        })
7752                    },
7753                    write: |settings_content, value| {
7754                        language_settings_field_mut(settings_content, value, |language, value| {
7755                            language.preferred_line_length = value;
7756                        })
7757                    },
7758                }),
7759                metadata: None,
7760                files: USER | PROJECT,
7761            }),
7762            SettingsPageItem::SettingItem(SettingItem {
7763                title: "Wrap Guides",
7764                description: "Character counts at which to show wrap guides in the editor.",
7765                field: Box::new(
7766                    SettingField {
7767                        json_path: Some("languages.$(language).wrap_guides"),
7768                        pick: |settings_content| {
7769                            language_settings_field(settings_content, |language| {
7770                                language.wrap_guides.as_ref()
7771                            })
7772                        },
7773                        write: |settings_content, value| {
7774                            language_settings_field_mut(
7775                                settings_content,
7776                                value,
7777                                |language, value| {
7778                                    language.wrap_guides = value;
7779                                },
7780                            )
7781                        },
7782                    }
7783                    .unimplemented(),
7784                ),
7785                metadata: None,
7786                files: USER | PROJECT,
7787            }),
7788            SettingsPageItem::SettingItem(SettingItem {
7789                title: "Allow Rewrap",
7790                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7791                field: Box::new(SettingField {
7792                    json_path: Some("languages.$(language).allow_rewrap"),
7793                    pick: |settings_content| {
7794                        language_settings_field(settings_content, |language| {
7795                            language.allow_rewrap.as_ref()
7796                        })
7797                    },
7798                    write: |settings_content, value| {
7799                        language_settings_field_mut(settings_content, value, |language, value| {
7800                            language.allow_rewrap = value;
7801                        })
7802                    },
7803                }),
7804                metadata: None,
7805                files: USER | PROJECT,
7806            }),
7807        ]
7808    }
7809
7810    fn indent_guides_section() -> [SettingsPageItem; 6] {
7811        [
7812            SettingsPageItem::SectionHeader("Indent Guides"),
7813            SettingsPageItem::SettingItem(SettingItem {
7814                title: "Enabled",
7815                description: "Display indent guides in the editor.",
7816                field: Box::new(SettingField {
7817                    json_path: Some("languages.$(language).indent_guides.enabled"),
7818                    pick: |settings_content| {
7819                        language_settings_field(settings_content, |language| {
7820                            language
7821                                .indent_guides
7822                                .as_ref()
7823                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7824                        })
7825                    },
7826                    write: |settings_content, value| {
7827                        language_settings_field_mut(settings_content, value, |language, value| {
7828                            language.indent_guides.get_or_insert_default().enabled = value;
7829                        })
7830                    },
7831                }),
7832                metadata: None,
7833                files: USER | PROJECT,
7834            }),
7835            SettingsPageItem::SettingItem(SettingItem {
7836                title: "Line Width",
7837                description: "The width of the indent guides in pixels, between 1 and 10.",
7838                field: Box::new(SettingField {
7839                    json_path: Some("languages.$(language).indent_guides.line_width"),
7840                    pick: |settings_content| {
7841                        language_settings_field(settings_content, |language| {
7842                            language
7843                                .indent_guides
7844                                .as_ref()
7845                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7846                        })
7847                    },
7848                    write: |settings_content, value| {
7849                        language_settings_field_mut(settings_content, value, |language, value| {
7850                            language.indent_guides.get_or_insert_default().line_width = value;
7851                        })
7852                    },
7853                }),
7854                metadata: None,
7855                files: USER | PROJECT,
7856            }),
7857            SettingsPageItem::SettingItem(SettingItem {
7858                title: "Active Line Width",
7859                description: "The width of the active indent guide in pixels, between 1 and 10.",
7860                field: Box::new(SettingField {
7861                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7862                    pick: |settings_content| {
7863                        language_settings_field(settings_content, |language| {
7864                            language
7865                                .indent_guides
7866                                .as_ref()
7867                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7868                        })
7869                    },
7870                    write: |settings_content, value| {
7871                        language_settings_field_mut(settings_content, value, |language, value| {
7872                            language
7873                                .indent_guides
7874                                .get_or_insert_default()
7875                                .active_line_width = value;
7876                        })
7877                    },
7878                }),
7879                metadata: None,
7880                files: USER | PROJECT,
7881            }),
7882            SettingsPageItem::SettingItem(SettingItem {
7883                title: "Coloring",
7884                description: "Determines how indent guides are colored.",
7885                field: Box::new(SettingField {
7886                    json_path: Some("languages.$(language).indent_guides.coloring"),
7887                    pick: |settings_content| {
7888                        language_settings_field(settings_content, |language| {
7889                            language
7890                                .indent_guides
7891                                .as_ref()
7892                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7893                        })
7894                    },
7895                    write: |settings_content, value| {
7896                        language_settings_field_mut(settings_content, value, |language, value| {
7897                            language.indent_guides.get_or_insert_default().coloring = value;
7898                        })
7899                    },
7900                }),
7901                metadata: None,
7902                files: USER | PROJECT,
7903            }),
7904            SettingsPageItem::SettingItem(SettingItem {
7905                title: "Background Coloring",
7906                description: "Determines how indent guide backgrounds are colored.",
7907                field: Box::new(SettingField {
7908                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7909                    pick: |settings_content| {
7910                        language_settings_field(settings_content, |language| {
7911                            language.indent_guides.as_ref().and_then(|indent_guides| {
7912                                indent_guides.background_coloring.as_ref()
7913                            })
7914                        })
7915                    },
7916                    write: |settings_content, value| {
7917                        language_settings_field_mut(settings_content, value, |language, value| {
7918                            language
7919                                .indent_guides
7920                                .get_or_insert_default()
7921                                .background_coloring = value;
7922                        })
7923                    },
7924                }),
7925                metadata: None,
7926                files: USER | PROJECT,
7927            }),
7928        ]
7929    }
7930
7931    fn formatting_section() -> [SettingsPageItem; 7] {
7932        [
7933            SettingsPageItem::SectionHeader("Formatting"),
7934            SettingsPageItem::SettingItem(SettingItem {
7935                title: "Format On Save",
7936                description: "Whether or not to perform a buffer format before saving.",
7937                field: Box::new(
7938                    // TODO(settings_ui): this setting should just be a bool
7939                    SettingField {
7940                        json_path: Some("languages.$(language).format_on_save"),
7941                        pick: |settings_content| {
7942                            language_settings_field(settings_content, |language| {
7943                                language.format_on_save.as_ref()
7944                            })
7945                        },
7946                        write: |settings_content, value| {
7947                            language_settings_field_mut(
7948                                settings_content,
7949                                value,
7950                                |language, value| {
7951                                    language.format_on_save = value;
7952                                },
7953                            )
7954                        },
7955                    },
7956                ),
7957                metadata: None,
7958                files: USER | PROJECT,
7959            }),
7960            SettingsPageItem::SettingItem(SettingItem {
7961                title: "Remove Trailing Whitespace On Save",
7962                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7963                field: Box::new(SettingField {
7964                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7965                    pick: |settings_content| {
7966                        language_settings_field(settings_content, |language| {
7967                            language.remove_trailing_whitespace_on_save.as_ref()
7968                        })
7969                    },
7970                    write: |settings_content, value| {
7971                        language_settings_field_mut(settings_content, value, |language, value| {
7972                            language.remove_trailing_whitespace_on_save = value;
7973                        })
7974                    },
7975                }),
7976                metadata: None,
7977                files: USER | PROJECT,
7978            }),
7979            SettingsPageItem::SettingItem(SettingItem {
7980                title: "Ensure Final Newline On Save",
7981                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7982                field: Box::new(SettingField {
7983                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7984                    pick: |settings_content| {
7985                        language_settings_field(settings_content, |language| {
7986                            language.ensure_final_newline_on_save.as_ref()
7987                        })
7988                    },
7989                    write: |settings_content, value| {
7990                        language_settings_field_mut(settings_content, value, |language, value| {
7991                            language.ensure_final_newline_on_save = value;
7992                        })
7993                    },
7994                }),
7995                metadata: None,
7996                files: USER | PROJECT,
7997            }),
7998            SettingsPageItem::SettingItem(SettingItem {
7999                title: "Formatter",
8000                description: "How to perform a buffer format.",
8001                field: Box::new(
8002                    SettingField {
8003                        json_path: Some("languages.$(language).formatter"),
8004                        pick: |settings_content| {
8005                            language_settings_field(settings_content, |language| {
8006                                language.formatter.as_ref()
8007                            })
8008                        },
8009                        write: |settings_content, value| {
8010                            language_settings_field_mut(
8011                                settings_content,
8012                                value,
8013                                |language, value| {
8014                                    language.formatter = value;
8015                                },
8016                            )
8017                        },
8018                    }
8019                    .unimplemented(),
8020                ),
8021                metadata: None,
8022                files: USER | PROJECT,
8023            }),
8024            SettingsPageItem::SettingItem(SettingItem {
8025                title: "Use On Type Format",
8026                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
8027                field: Box::new(SettingField {
8028                    json_path: Some("languages.$(language).use_on_type_format"),
8029                    pick: |settings_content| {
8030                        language_settings_field(settings_content, |language| {
8031                            language.use_on_type_format.as_ref()
8032                        })
8033                    },
8034                    write: |settings_content, value| {
8035                        language_settings_field_mut(settings_content, value, |language, value| {
8036                            language.use_on_type_format = value;
8037                        })
8038                    },
8039                }),
8040                metadata: None,
8041                files: USER | PROJECT,
8042            }),
8043            SettingsPageItem::SettingItem(SettingItem {
8044                title: "Code Actions On Format",
8045                description: "Additional code actions to run when formatting.",
8046                field: Box::new(
8047                    SettingField {
8048                        json_path: Some("languages.$(language).code_actions_on_format"),
8049                        pick: |settings_content| {
8050                            language_settings_field(settings_content, |language| {
8051                                language.code_actions_on_format.as_ref()
8052                            })
8053                        },
8054                        write: |settings_content, value| {
8055                            language_settings_field_mut(
8056                                settings_content,
8057                                value,
8058                                |language, value| {
8059                                    language.code_actions_on_format = value;
8060                                },
8061                            )
8062                        },
8063                    }
8064                    .unimplemented(),
8065                ),
8066                metadata: None,
8067                files: USER | PROJECT,
8068            }),
8069        ]
8070    }
8071
8072    fn autoclose_section() -> [SettingsPageItem; 5] {
8073        [
8074            SettingsPageItem::SectionHeader("Autoclose"),
8075            SettingsPageItem::SettingItem(SettingItem {
8076                title: "Use Autoclose",
8077                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
8078                field: Box::new(SettingField {
8079                    json_path: Some("languages.$(language).use_autoclose"),
8080                    pick: |settings_content| {
8081                        language_settings_field(settings_content, |language| {
8082                            language.use_autoclose.as_ref()
8083                        })
8084                    },
8085                    write: |settings_content, value| {
8086                        language_settings_field_mut(settings_content, value, |language, value| {
8087                            language.use_autoclose = value;
8088                        })
8089                    },
8090                }),
8091                metadata: None,
8092                files: USER | PROJECT,
8093            }),
8094            SettingsPageItem::SettingItem(SettingItem {
8095                title: "Use Auto Surround",
8096                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
8097                field: Box::new(SettingField {
8098                    json_path: Some("languages.$(language).use_auto_surround"),
8099                    pick: |settings_content| {
8100                        language_settings_field(settings_content, |language| {
8101                            language.use_auto_surround.as_ref()
8102                        })
8103                    },
8104                    write: |settings_content, value| {
8105                        language_settings_field_mut(settings_content, value, |language, value| {
8106                            language.use_auto_surround = value;
8107                        })
8108                    },
8109                }),
8110                metadata: None,
8111                files: USER | PROJECT,
8112            }),
8113            SettingsPageItem::SettingItem(SettingItem {
8114                title: "Always Treat Brackets As Autoclosed",
8115                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
8116                field: Box::new(SettingField {
8117                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
8118                    pick: |settings_content| {
8119                        language_settings_field(settings_content, |language| {
8120                            language.always_treat_brackets_as_autoclosed.as_ref()
8121                        })
8122                    },
8123                    write: |settings_content, value| {
8124                        language_settings_field_mut(settings_content, value, |language, value| {
8125                            language.always_treat_brackets_as_autoclosed = value;
8126                        })
8127                    },
8128                }),
8129                metadata: None,
8130                files: USER | PROJECT,
8131            }),
8132            SettingsPageItem::SettingItem(SettingItem {
8133                title: "JSX Tag Auto Close",
8134                description: "Whether to automatically close JSX tags.",
8135                field: Box::new(SettingField {
8136                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
8137                    // TODO(settings_ui): this setting should just be a bool
8138                    pick: |settings_content| {
8139                        language_settings_field(settings_content, |language| {
8140                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
8141                        })
8142                    },
8143                    write: |settings_content, value| {
8144                        language_settings_field_mut(settings_content, value, |language, value| {
8145                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
8146                        })
8147                    },
8148                }),
8149                metadata: None,
8150                files: USER | PROJECT,
8151            }),
8152        ]
8153    }
8154
8155    fn whitespace_section() -> [SettingsPageItem; 4] {
8156        [
8157            SettingsPageItem::SectionHeader("Whitespace"),
8158            SettingsPageItem::SettingItem(SettingItem {
8159                title: "Show Whitespaces",
8160                description: "Whether to show tabs and spaces in the editor.",
8161                field: Box::new(SettingField {
8162                    json_path: Some("languages.$(language).show_whitespaces"),
8163                    pick: |settings_content| {
8164                        language_settings_field(settings_content, |language| {
8165                            language.show_whitespaces.as_ref()
8166                        })
8167                    },
8168                    write: |settings_content, value| {
8169                        language_settings_field_mut(settings_content, value, |language, value| {
8170                            language.show_whitespaces = value;
8171                        })
8172                    },
8173                }),
8174                metadata: None,
8175                files: USER | PROJECT,
8176            }),
8177            SettingsPageItem::SettingItem(SettingItem {
8178                title: "Space Whitespace Indicator",
8179                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
8180                field: Box::new(
8181                    SettingField {
8182                        json_path: Some("languages.$(language).whitespace_map.space"),
8183                        pick: |settings_content| {
8184                            language_settings_field(settings_content, |language| {
8185                                language.whitespace_map.as_ref()?.space.as_ref()
8186                            })
8187                        },
8188                        write: |settings_content, value| {
8189                            language_settings_field_mut(
8190                                settings_content,
8191                                value,
8192                                |language, value| {
8193                                    language.whitespace_map.get_or_insert_default().space = value;
8194                                },
8195                            )
8196                        },
8197                    }
8198                    .unimplemented(),
8199                ),
8200                metadata: None,
8201                files: USER | PROJECT,
8202            }),
8203            SettingsPageItem::SettingItem(SettingItem {
8204                title: "Tab Whitespace Indicator",
8205                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
8206                field: Box::new(
8207                    SettingField {
8208                        json_path: Some("languages.$(language).whitespace_map.tab"),
8209                        pick: |settings_content| {
8210                            language_settings_field(settings_content, |language| {
8211                                language.whitespace_map.as_ref()?.tab.as_ref()
8212                            })
8213                        },
8214                        write: |settings_content, value| {
8215                            language_settings_field_mut(
8216                                settings_content,
8217                                value,
8218                                |language, value| {
8219                                    language.whitespace_map.get_or_insert_default().tab = value;
8220                                },
8221                            )
8222                        },
8223                    }
8224                    .unimplemented(),
8225                ),
8226                metadata: None,
8227                files: USER | PROJECT,
8228            }),
8229        ]
8230    }
8231
8232    fn completions_section() -> [SettingsPageItem; 7] {
8233        [
8234            SettingsPageItem::SectionHeader("Completions"),
8235            SettingsPageItem::SettingItem(SettingItem {
8236                title: "Show Completions On Input",
8237                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
8238                field: Box::new(SettingField {
8239                    json_path: Some("languages.$(language).show_completions_on_input"),
8240                    pick: |settings_content| {
8241                        language_settings_field(settings_content, |language| {
8242                            language.show_completions_on_input.as_ref()
8243                        })
8244                    },
8245                    write: |settings_content, value| {
8246                        language_settings_field_mut(settings_content, value, |language, value| {
8247                            language.show_completions_on_input = value;
8248                        })
8249                    },
8250                }),
8251                metadata: None,
8252                files: USER | PROJECT,
8253            }),
8254            SettingsPageItem::SettingItem(SettingItem {
8255                title: "Show Completion Documentation",
8256                description: "Whether to display inline and alongside documentation for items in the completions menu.",
8257                field: Box::new(SettingField {
8258                    json_path: Some("languages.$(language).show_completion_documentation"),
8259                    pick: |settings_content| {
8260                        language_settings_field(settings_content, |language| {
8261                            language.show_completion_documentation.as_ref()
8262                        })
8263                    },
8264                    write: |settings_content, value| {
8265                        language_settings_field_mut(settings_content, value, |language, value| {
8266                            language.show_completion_documentation = value;
8267                        })
8268                    },
8269                }),
8270                metadata: None,
8271                files: USER | PROJECT,
8272            }),
8273            SettingsPageItem::SettingItem(SettingItem {
8274                title: "Words",
8275                description: "Controls how words are completed.",
8276                field: Box::new(SettingField {
8277                    json_path: Some("languages.$(language).completions.words"),
8278                    pick: |settings_content| {
8279                        language_settings_field(settings_content, |language| {
8280                            language.completions.as_ref()?.words.as_ref()
8281                        })
8282                    },
8283                    write: |settings_content, value| {
8284                        language_settings_field_mut(settings_content, value, |language, value| {
8285                            language.completions.get_or_insert_default().words = value;
8286                        })
8287                    },
8288                }),
8289                metadata: None,
8290                files: USER | PROJECT,
8291            }),
8292            SettingsPageItem::SettingItem(SettingItem {
8293                title: "Words Min Length",
8294                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8295                field: Box::new(SettingField {
8296                    json_path: Some("languages.$(language).completions.words_min_length"),
8297                    pick: |settings_content| {
8298                        language_settings_field(settings_content, |language| {
8299                            language.completions.as_ref()?.words_min_length.as_ref()
8300                        })
8301                    },
8302                    write: |settings_content, value| {
8303                        language_settings_field_mut(settings_content, value, |language, value| {
8304                            language
8305                                .completions
8306                                .get_or_insert_default()
8307                                .words_min_length = value;
8308                        })
8309                    },
8310                }),
8311                metadata: None,
8312                files: USER | PROJECT,
8313            }),
8314            SettingsPageItem::SettingItem(SettingItem {
8315                title: "Completion Menu Scrollbar",
8316                description: "When to show the scrollbar in the completion menu.",
8317                field: Box::new(SettingField {
8318                    json_path: Some("editor.completion_menu_scrollbar"),
8319                    pick: |settings_content| {
8320                        settings_content.editor.completion_menu_scrollbar.as_ref()
8321                    },
8322                    write: |settings_content, value| {
8323                        settings_content.editor.completion_menu_scrollbar = value;
8324                    },
8325                }),
8326                metadata: None,
8327                files: USER,
8328            }),
8329            SettingsPageItem::SettingItem(SettingItem {
8330                title: "Completion Detail Alignment",
8331                description: "Whether to align detail text in code completions context menus left or right.",
8332                field: Box::new(SettingField {
8333                    json_path: Some("editor.completion_detail_alignment"),
8334                    pick: |settings_content| {
8335                        settings_content.editor.completion_detail_alignment.as_ref()
8336                    },
8337                    write: |settings_content, value| {
8338                        settings_content.editor.completion_detail_alignment = value;
8339                    },
8340                }),
8341                metadata: None,
8342                files: USER,
8343            }),
8344        ]
8345    }
8346
8347    fn inlay_hints_section() -> [SettingsPageItem; 10] {
8348        [
8349            SettingsPageItem::SectionHeader("Inlay Hints"),
8350            SettingsPageItem::SettingItem(SettingItem {
8351                title: "Enabled",
8352                description: "Global switch to toggle hints on and off.",
8353                field: Box::new(SettingField {
8354                    json_path: Some("languages.$(language).inlay_hints.enabled"),
8355                    pick: |settings_content| {
8356                        language_settings_field(settings_content, |language| {
8357                            language.inlay_hints.as_ref()?.enabled.as_ref()
8358                        })
8359                    },
8360                    write: |settings_content, value| {
8361                        language_settings_field_mut(settings_content, value, |language, value| {
8362                            language.inlay_hints.get_or_insert_default().enabled = value;
8363                        })
8364                    },
8365                }),
8366                metadata: None,
8367                files: USER | PROJECT,
8368            }),
8369            SettingsPageItem::SettingItem(SettingItem {
8370                title: "Show Value Hints",
8371                description: "Global switch to toggle inline values on and off when debugging.",
8372                field: Box::new(SettingField {
8373                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8374                    pick: |settings_content| {
8375                        language_settings_field(settings_content, |language| {
8376                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8377                        })
8378                    },
8379                    write: |settings_content, value| {
8380                        language_settings_field_mut(settings_content, value, |language, value| {
8381                            language
8382                                .inlay_hints
8383                                .get_or_insert_default()
8384                                .show_value_hints = value;
8385                        })
8386                    },
8387                }),
8388                metadata: None,
8389                files: USER | PROJECT,
8390            }),
8391            SettingsPageItem::SettingItem(SettingItem {
8392                title: "Show Type Hints",
8393                description: "Whether type hints should be shown.",
8394                field: Box::new(SettingField {
8395                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8396                    pick: |settings_content| {
8397                        language_settings_field(settings_content, |language| {
8398                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8399                        })
8400                    },
8401                    write: |settings_content, value| {
8402                        language_settings_field_mut(settings_content, value, |language, value| {
8403                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8404                        })
8405                    },
8406                }),
8407                metadata: None,
8408                files: USER | PROJECT,
8409            }),
8410            SettingsPageItem::SettingItem(SettingItem {
8411                title: "Show Parameter Hints",
8412                description: "Whether parameter hints should be shown.",
8413                field: Box::new(SettingField {
8414                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8415                    pick: |settings_content| {
8416                        language_settings_field(settings_content, |language| {
8417                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8418                        })
8419                    },
8420                    write: |settings_content, value| {
8421                        language_settings_field_mut(settings_content, value, |language, value| {
8422                            language
8423                                .inlay_hints
8424                                .get_or_insert_default()
8425                                .show_parameter_hints = value;
8426                        })
8427                    },
8428                }),
8429                metadata: None,
8430                files: USER | PROJECT,
8431            }),
8432            SettingsPageItem::SettingItem(SettingItem {
8433                title: "Show Other Hints",
8434                description: "Whether other hints should be shown.",
8435                field: Box::new(SettingField {
8436                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8437                    pick: |settings_content| {
8438                        language_settings_field(settings_content, |language| {
8439                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8440                        })
8441                    },
8442                    write: |settings_content, value| {
8443                        language_settings_field_mut(settings_content, value, |language, value| {
8444                            language
8445                                .inlay_hints
8446                                .get_or_insert_default()
8447                                .show_other_hints = value;
8448                        })
8449                    },
8450                }),
8451                metadata: None,
8452                files: USER | PROJECT,
8453            }),
8454            SettingsPageItem::SettingItem(SettingItem {
8455                title: "Show Background",
8456                description: "Show a background for inlay hints.",
8457                field: Box::new(SettingField {
8458                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8459                    pick: |settings_content| {
8460                        language_settings_field(settings_content, |language| {
8461                            language.inlay_hints.as_ref()?.show_background.as_ref()
8462                        })
8463                    },
8464                    write: |settings_content, value| {
8465                        language_settings_field_mut(settings_content, value, |language, value| {
8466                            language.inlay_hints.get_or_insert_default().show_background = value;
8467                        })
8468                    },
8469                }),
8470                metadata: None,
8471                files: USER | PROJECT,
8472            }),
8473            SettingsPageItem::SettingItem(SettingItem {
8474                title: "Edit Debounce Ms",
8475                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8476                field: Box::new(SettingField {
8477                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8478                    pick: |settings_content| {
8479                        language_settings_field(settings_content, |language| {
8480                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8481                        })
8482                    },
8483                    write: |settings_content, value| {
8484                        language_settings_field_mut(settings_content, value, |language, value| {
8485                            language
8486                                .inlay_hints
8487                                .get_or_insert_default()
8488                                .edit_debounce_ms = value;
8489                        })
8490                    },
8491                }),
8492                metadata: None,
8493                files: USER | PROJECT,
8494            }),
8495            SettingsPageItem::SettingItem(SettingItem {
8496                title: "Scroll Debounce Ms",
8497                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8498                field: Box::new(SettingField {
8499                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8500                    pick: |settings_content| {
8501                        language_settings_field(settings_content, |language| {
8502                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8503                        })
8504                    },
8505                    write: |settings_content, value| {
8506                        language_settings_field_mut(settings_content, value, |language, value| {
8507                            language
8508                                .inlay_hints
8509                                .get_or_insert_default()
8510                                .scroll_debounce_ms = value;
8511                        })
8512                    },
8513                }),
8514                metadata: None,
8515                files: USER | PROJECT,
8516            }),
8517            SettingsPageItem::SettingItem(SettingItem {
8518                title: "Toggle On Modifiers Press",
8519                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8520                field: Box::new(
8521                    SettingField {
8522                        json_path: Some(
8523                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8524                        ),
8525                        pick: |settings_content| {
8526                            language_settings_field(settings_content, |language| {
8527                                language
8528                                    .inlay_hints
8529                                    .as_ref()?
8530                                    .toggle_on_modifiers_press
8531                                    .as_ref()
8532                            })
8533                        },
8534                        write: |settings_content, value| {
8535                            language_settings_field_mut(
8536                                settings_content,
8537                                value,
8538                                |language, value| {
8539                                    language
8540                                        .inlay_hints
8541                                        .get_or_insert_default()
8542                                        .toggle_on_modifiers_press = value;
8543                                },
8544                            )
8545                        },
8546                    }
8547                    .unimplemented(),
8548                ),
8549                metadata: None,
8550                files: USER | PROJECT,
8551            }),
8552        ]
8553    }
8554
8555    fn tasks_section() -> [SettingsPageItem; 4] {
8556        [
8557            SettingsPageItem::SectionHeader("Tasks"),
8558            SettingsPageItem::SettingItem(SettingItem {
8559                title: "Enabled",
8560                description: "Whether tasks are enabled for this language.",
8561                field: Box::new(SettingField {
8562                    json_path: Some("languages.$(language).tasks.enabled"),
8563                    pick: |settings_content| {
8564                        language_settings_field(settings_content, |language| {
8565                            language.tasks.as_ref()?.enabled.as_ref()
8566                        })
8567                    },
8568                    write: |settings_content, value| {
8569                        language_settings_field_mut(settings_content, value, |language, value| {
8570                            language.tasks.get_or_insert_default().enabled = value;
8571                        })
8572                    },
8573                }),
8574                metadata: None,
8575                files: USER | PROJECT,
8576            }),
8577            SettingsPageItem::SettingItem(SettingItem {
8578                title: "Variables",
8579                description: "Extra task variables to set for a particular language.",
8580                field: Box::new(
8581                    SettingField {
8582                        json_path: Some("languages.$(language).tasks.variables"),
8583                        pick: |settings_content| {
8584                            language_settings_field(settings_content, |language| {
8585                                language.tasks.as_ref()?.variables.as_ref()
8586                            })
8587                        },
8588                        write: |settings_content, value| {
8589                            language_settings_field_mut(
8590                                settings_content,
8591                                value,
8592                                |language, value| {
8593                                    language.tasks.get_or_insert_default().variables = value;
8594                                },
8595                            )
8596                        },
8597                    }
8598                    .unimplemented(),
8599                ),
8600                metadata: None,
8601                files: USER | PROJECT,
8602            }),
8603            SettingsPageItem::SettingItem(SettingItem {
8604                title: "Prefer LSP",
8605                description: "Use LSP tasks over Zed language extension tasks.",
8606                field: Box::new(SettingField {
8607                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8608                    pick: |settings_content| {
8609                        language_settings_field(settings_content, |language| {
8610                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8611                        })
8612                    },
8613                    write: |settings_content, value| {
8614                        language_settings_field_mut(settings_content, value, |language, value| {
8615                            language.tasks.get_or_insert_default().prefer_lsp = value;
8616                        })
8617                    },
8618                }),
8619                metadata: None,
8620                files: USER | PROJECT,
8621            }),
8622        ]
8623    }
8624
8625    fn miscellaneous_section() -> [SettingsPageItem; 7] {
8626        [
8627            SettingsPageItem::SectionHeader("Miscellaneous"),
8628            SettingsPageItem::SettingItem(SettingItem {
8629                title: "Word Diff Enabled",
8630                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8631                field: Box::new(SettingField {
8632                    json_path: Some("languages.$(language).word_diff_enabled"),
8633                    pick: |settings_content| {
8634                        language_settings_field(settings_content, |language| {
8635                            language.word_diff_enabled.as_ref()
8636                        })
8637                    },
8638                    write: |settings_content, value| {
8639                        language_settings_field_mut(settings_content, value, |language, value| {
8640                            language.word_diff_enabled = value;
8641                        })
8642                    },
8643                }),
8644                metadata: None,
8645                files: USER | PROJECT,
8646            }),
8647            SettingsPageItem::SettingItem(SettingItem {
8648                title: "Debuggers",
8649                description: "Preferred debuggers for this language.",
8650                field: Box::new(
8651                    SettingField {
8652                        json_path: Some("languages.$(language).debuggers"),
8653                        pick: |settings_content| {
8654                            language_settings_field(settings_content, |language| {
8655                                language.debuggers.as_ref()
8656                            })
8657                        },
8658                        write: |settings_content, value| {
8659                            language_settings_field_mut(
8660                                settings_content,
8661                                value,
8662                                |language, value| {
8663                                    language.debuggers = value;
8664                                },
8665                            )
8666                        },
8667                    }
8668                    .unimplemented(),
8669                ),
8670                metadata: None,
8671                files: USER | PROJECT,
8672            }),
8673            SettingsPageItem::SettingItem(SettingItem {
8674                title: "Middle Click Paste",
8675                description: "Enable middle-click paste on Linux.",
8676                field: Box::new(SettingField {
8677                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8678                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8679                    write: |settings_content, value| {
8680                        settings_content.editor.middle_click_paste = value;
8681                    },
8682                }),
8683                metadata: None,
8684                files: USER,
8685            }),
8686            SettingsPageItem::SettingItem(SettingItem {
8687                title: "Extend Comment On Newline",
8688                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8689                field: Box::new(SettingField {
8690                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8691                    pick: |settings_content| {
8692                        language_settings_field(settings_content, |language| {
8693                            language.extend_comment_on_newline.as_ref()
8694                        })
8695                    },
8696                    write: |settings_content, value| {
8697                        language_settings_field_mut(settings_content, value, |language, value| {
8698                            language.extend_comment_on_newline = value;
8699                        })
8700                    },
8701                }),
8702                metadata: None,
8703                files: USER | PROJECT,
8704            }),
8705            SettingsPageItem::SettingItem(SettingItem {
8706                title: "Colorize Brackets",
8707                description: "Whether to colorize brackets in the editor.",
8708                field: Box::new(SettingField {
8709                    json_path: Some("languages.$(language).colorize_brackets"),
8710                    pick: |settings_content| {
8711                        language_settings_field(settings_content, |language| {
8712                            language.colorize_brackets.as_ref()
8713                        })
8714                    },
8715                    write: |settings_content, value| {
8716                        language_settings_field_mut(settings_content, value, |language, value| {
8717                            language.colorize_brackets = value;
8718                        })
8719                    },
8720                }),
8721                metadata: None,
8722                files: USER | PROJECT,
8723            }),
8724            SettingsPageItem::SettingItem(SettingItem {
8725                title: "Vim/Emacs Modeline Support",
8726                description: "Number of lines to search for modelines (set to 0 to disable).",
8727                field: Box::new(SettingField {
8728                    json_path: Some("modeline_lines"),
8729                    pick: |settings_content| settings_content.modeline_lines.as_ref(),
8730                    write: |settings_content, value| {
8731                        settings_content.modeline_lines = value;
8732                    },
8733                }),
8734                metadata: None,
8735                files: USER | PROJECT,
8736            }),
8737        ]
8738    }
8739
8740    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8741        [
8742            SettingsPageItem::SettingItem(SettingItem {
8743                title: "Image Viewer",
8744                description: "The unit for image file sizes.",
8745                field: Box::new(SettingField {
8746                    json_path: Some("image_viewer.unit"),
8747                    pick: |settings_content| {
8748                        settings_content
8749                            .image_viewer
8750                            .as_ref()
8751                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8752                    },
8753                    write: |settings_content, value| {
8754                        settings_content.image_viewer.get_or_insert_default().unit = value;
8755                    },
8756                }),
8757                metadata: None,
8758                files: USER,
8759            }),
8760            SettingsPageItem::SettingItem(SettingItem {
8761                title: "Auto Replace Emoji Shortcode",
8762                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8763                field: Box::new(SettingField {
8764                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8765                    pick: |settings_content| {
8766                        settings_content
8767                            .message_editor
8768                            .as_ref()
8769                            .and_then(|message_editor| {
8770                                message_editor.auto_replace_emoji_shortcode.as_ref()
8771                            })
8772                    },
8773                    write: |settings_content, value| {
8774                        settings_content
8775                            .message_editor
8776                            .get_or_insert_default()
8777                            .auto_replace_emoji_shortcode = value;
8778                    },
8779                }),
8780                metadata: None,
8781                files: USER,
8782            }),
8783            SettingsPageItem::SettingItem(SettingItem {
8784                title: "Drop Size Target",
8785                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8786                field: Box::new(SettingField {
8787                    json_path: Some("drop_target_size"),
8788                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8789                    write: |settings_content, value| {
8790                        settings_content.workspace.drop_target_size = value;
8791                    },
8792                }),
8793                metadata: None,
8794                files: USER,
8795            }),
8796        ]
8797    }
8798
8799    let is_global = active_language().is_none();
8800
8801    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8802        title: "LSP Document Colors",
8803        description: "How to render LSP color previews in the editor.",
8804        field: Box::new(SettingField {
8805            json_path: Some("lsp_document_colors"),
8806            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8807            write: |settings_content, value| {
8808                settings_content.editor.lsp_document_colors = value;
8809            },
8810        }),
8811        metadata: None,
8812        files: USER,
8813    })];
8814
8815    if is_global {
8816        concat_sections!(
8817            indentation_section(),
8818            wrapping_section(),
8819            indent_guides_section(),
8820            formatting_section(),
8821            autoclose_section(),
8822            whitespace_section(),
8823            completions_section(),
8824            inlay_hints_section(),
8825            lsp_document_colors_item,
8826            tasks_section(),
8827            miscellaneous_section(),
8828            global_only_miscellaneous_sub_section(),
8829        )
8830    } else {
8831        concat_sections!(
8832            indentation_section(),
8833            wrapping_section(),
8834            indent_guides_section(),
8835            formatting_section(),
8836            autoclose_section(),
8837            whitespace_section(),
8838            completions_section(),
8839            inlay_hints_section(),
8840            tasks_section(),
8841            miscellaneous_section(),
8842        )
8843    }
8844}
8845
8846/// LanguageSettings items that should be included in the "Languages & Tools" page
8847/// not the "Editor" page
8848fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8849    fn lsp_section() -> [SettingsPageItem; 8] {
8850        [
8851            SettingsPageItem::SectionHeader("LSP"),
8852            SettingsPageItem::SettingItem(SettingItem {
8853                title: "Enable Language Server",
8854                description: "Whether to use language servers to provide code intelligence.",
8855                field: Box::new(SettingField {
8856                    json_path: Some("languages.$(language).enable_language_server"),
8857                    pick: |settings_content| {
8858                        language_settings_field(settings_content, |language| {
8859                            language.enable_language_server.as_ref()
8860                        })
8861                    },
8862                    write: |settings_content, value| {
8863                        language_settings_field_mut(settings_content, value, |language, value| {
8864                            language.enable_language_server = value;
8865                        })
8866                    },
8867                }),
8868                metadata: None,
8869                files: USER | PROJECT,
8870            }),
8871            SettingsPageItem::SettingItem(SettingItem {
8872                title: "Language Servers",
8873                description: "The list of language servers to use (or disable) for this language.",
8874                field: Box::new(
8875                    SettingField {
8876                        json_path: Some("languages.$(language).language_servers"),
8877                        pick: |settings_content| {
8878                            language_settings_field(settings_content, |language| {
8879                                language.language_servers.as_ref()
8880                            })
8881                        },
8882                        write: |settings_content, value| {
8883                            language_settings_field_mut(
8884                                settings_content,
8885                                value,
8886                                |language, value| {
8887                                    language.language_servers = value;
8888                                },
8889                            )
8890                        },
8891                    }
8892                    .unimplemented(),
8893                ),
8894                metadata: None,
8895                files: USER | PROJECT,
8896            }),
8897            SettingsPageItem::SettingItem(SettingItem {
8898                title: "Linked Edits",
8899                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.",
8900                field: Box::new(SettingField {
8901                    json_path: Some("languages.$(language).linked_edits"),
8902                    pick: |settings_content| {
8903                        language_settings_field(settings_content, |language| {
8904                            language.linked_edits.as_ref()
8905                        })
8906                    },
8907                    write: |settings_content, value| {
8908                        language_settings_field_mut(settings_content, value, |language, value| {
8909                            language.linked_edits = value;
8910                        })
8911                    },
8912                }),
8913                metadata: None,
8914                files: USER | PROJECT,
8915            }),
8916            SettingsPageItem::SettingItem(SettingItem {
8917                title: "Go To Definition Fallback",
8918                description: "Whether to follow-up empty Go to definition responses from the language server.",
8919                field: Box::new(SettingField {
8920                    json_path: Some("go_to_definition_fallback"),
8921                    pick: |settings_content| {
8922                        settings_content.editor.go_to_definition_fallback.as_ref()
8923                    },
8924                    write: |settings_content, value| {
8925                        settings_content.editor.go_to_definition_fallback = value;
8926                    },
8927                }),
8928                metadata: None,
8929                files: USER,
8930            }),
8931            SettingsPageItem::SettingItem(SettingItem {
8932                title: "Semantic Tokens",
8933                description: {
8934                    static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
8935                    DESCRIPTION.get_or_init(|| {
8936                        SemanticTokens::VARIANTS
8937                            .iter()
8938                            .filter_map(|v| {
8939                                v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
8940                            })
8941                            .join("\n")
8942                            .leak()
8943                    })
8944                },
8945                field: Box::new(SettingField {
8946                    json_path: Some("languages.$(language).semantic_tokens"),
8947                    pick: |settings_content| {
8948                        settings_content
8949                            .project
8950                            .all_languages
8951                            .defaults
8952                            .semantic_tokens
8953                            .as_ref()
8954                    },
8955                    write: |settings_content, value| {
8956                        settings_content
8957                            .project
8958                            .all_languages
8959                            .defaults
8960                            .semantic_tokens = value;
8961                    },
8962                }),
8963                metadata: None,
8964                files: USER | PROJECT,
8965            }),
8966            SettingsPageItem::SettingItem(SettingItem {
8967                title: "LSP Folding Ranges",
8968                description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
8969                field: Box::new(SettingField {
8970                    json_path: Some("languages.$(language).document_folding_ranges"),
8971                    pick: |settings_content| {
8972                        language_settings_field(settings_content, |language| {
8973                            language.document_folding_ranges.as_ref()
8974                        })
8975                    },
8976                    write: |settings_content, value| {
8977                        language_settings_field_mut(settings_content, value, |language, value| {
8978                            language.document_folding_ranges = value;
8979                        })
8980                    },
8981                }),
8982                metadata: None,
8983                files: USER | PROJECT,
8984            }),
8985            SettingsPageItem::SettingItem(SettingItem {
8986                title: "LSP Document Symbols",
8987                description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
8988                field: Box::new(SettingField {
8989                    json_path: Some("languages.$(language).document_symbols"),
8990                    pick: |settings_content| {
8991                        language_settings_field(settings_content, |language| {
8992                            language.document_symbols.as_ref()
8993                        })
8994                    },
8995                    write: |settings_content, value| {
8996                        language_settings_field_mut(settings_content, value, |language, value| {
8997                            language.document_symbols = value;
8998                        })
8999                    },
9000                }),
9001                metadata: None,
9002                files: USER | PROJECT,
9003            }),
9004        ]
9005    }
9006
9007    fn lsp_completions_section() -> [SettingsPageItem; 4] {
9008        [
9009            SettingsPageItem::SectionHeader("LSP Completions"),
9010            SettingsPageItem::SettingItem(SettingItem {
9011                title: "Enabled",
9012                description: "Whether to fetch LSP completions or not.",
9013                field: Box::new(SettingField {
9014                    json_path: Some("languages.$(language).completions.lsp"),
9015                    pick: |settings_content| {
9016                        language_settings_field(settings_content, |language| {
9017                            language.completions.as_ref()?.lsp.as_ref()
9018                        })
9019                    },
9020                    write: |settings_content, value| {
9021                        language_settings_field_mut(settings_content, value, |language, value| {
9022                            language.completions.get_or_insert_default().lsp = value;
9023                        })
9024                    },
9025                }),
9026                metadata: None,
9027                files: USER | PROJECT,
9028            }),
9029            SettingsPageItem::SettingItem(SettingItem {
9030                title: "Fetch Timeout (milliseconds)",
9031                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
9032                field: Box::new(SettingField {
9033                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
9034                    pick: |settings_content| {
9035                        language_settings_field(settings_content, |language| {
9036                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
9037                        })
9038                    },
9039                    write: |settings_content, value| {
9040                        language_settings_field_mut(settings_content, value, |language, value| {
9041                            language
9042                                .completions
9043                                .get_or_insert_default()
9044                                .lsp_fetch_timeout_ms = value;
9045                        })
9046                    },
9047                }),
9048                metadata: None,
9049                files: USER | PROJECT,
9050            }),
9051            SettingsPageItem::SettingItem(SettingItem {
9052                title: "Insert Mode",
9053                description: "Controls how LSP completions are inserted.",
9054                field: Box::new(SettingField {
9055                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
9056                    pick: |settings_content| {
9057                        language_settings_field(settings_content, |language| {
9058                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
9059                        })
9060                    },
9061                    write: |settings_content, value| {
9062                        language_settings_field_mut(settings_content, value, |language, value| {
9063                            language.completions.get_or_insert_default().lsp_insert_mode = value;
9064                        })
9065                    },
9066                }),
9067                metadata: None,
9068                files: USER | PROJECT,
9069            }),
9070        ]
9071    }
9072
9073    fn debugger_section() -> [SettingsPageItem; 2] {
9074        [
9075            SettingsPageItem::SectionHeader("Debuggers"),
9076            SettingsPageItem::SettingItem(SettingItem {
9077                title: "Debuggers",
9078                description: "Preferred debuggers for this language.",
9079                field: Box::new(
9080                    SettingField {
9081                        json_path: Some("languages.$(language).debuggers"),
9082                        pick: |settings_content| {
9083                            language_settings_field(settings_content, |language| {
9084                                language.debuggers.as_ref()
9085                            })
9086                        },
9087                        write: |settings_content, value| {
9088                            language_settings_field_mut(
9089                                settings_content,
9090                                value,
9091                                |language, value| {
9092                                    language.debuggers = value;
9093                                },
9094                            )
9095                        },
9096                    }
9097                    .unimplemented(),
9098                ),
9099                metadata: None,
9100                files: USER | PROJECT,
9101            }),
9102        ]
9103    }
9104
9105    fn prettier_section() -> [SettingsPageItem; 5] {
9106        [
9107            SettingsPageItem::SectionHeader("Prettier"),
9108            SettingsPageItem::SettingItem(SettingItem {
9109                title: "Allowed",
9110                description: "Enables or disables formatting with Prettier for a given language.",
9111                field: Box::new(SettingField {
9112                    json_path: Some("languages.$(language).prettier.allowed"),
9113                    pick: |settings_content| {
9114                        language_settings_field(settings_content, |language| {
9115                            language.prettier.as_ref()?.allowed.as_ref()
9116                        })
9117                    },
9118                    write: |settings_content, value| {
9119                        language_settings_field_mut(settings_content, value, |language, value| {
9120                            language.prettier.get_or_insert_default().allowed = value;
9121                        })
9122                    },
9123                }),
9124                metadata: None,
9125                files: USER | PROJECT,
9126            }),
9127            SettingsPageItem::SettingItem(SettingItem {
9128                title: "Parser",
9129                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
9130                field: Box::new(SettingField {
9131                    json_path: Some("languages.$(language).prettier.parser"),
9132                    pick: |settings_content| {
9133                        language_settings_field(settings_content, |language| {
9134                            language.prettier.as_ref()?.parser.as_ref()
9135                        })
9136                    },
9137                    write: |settings_content, value| {
9138                        language_settings_field_mut(settings_content, value, |language, value| {
9139                            language.prettier.get_or_insert_default().parser = value;
9140                        })
9141                    },
9142                }),
9143                metadata: None,
9144                files: USER | PROJECT,
9145            }),
9146            SettingsPageItem::SettingItem(SettingItem {
9147                title: "Plugins",
9148                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
9149                field: Box::new(
9150                    SettingField {
9151                        json_path: Some("languages.$(language).prettier.plugins"),
9152                        pick: |settings_content| {
9153                            language_settings_field(settings_content, |language| {
9154                                language.prettier.as_ref()?.plugins.as_ref()
9155                            })
9156                        },
9157                        write: |settings_content, value| {
9158                            language_settings_field_mut(
9159                                settings_content,
9160                                value,
9161                                |language, value| {
9162                                    language.prettier.get_or_insert_default().plugins = value;
9163                                },
9164                            )
9165                        },
9166                    }
9167                    .unimplemented(),
9168                ),
9169                metadata: None,
9170                files: USER | PROJECT,
9171            }),
9172            SettingsPageItem::SettingItem(SettingItem {
9173                title: "Options",
9174                description: "Default Prettier options, in the format as in package.json section for Prettier.",
9175                field: Box::new(
9176                    SettingField {
9177                        json_path: Some("languages.$(language).prettier.options"),
9178                        pick: |settings_content| {
9179                            language_settings_field(settings_content, |language| {
9180                                language.prettier.as_ref()?.options.as_ref()
9181                            })
9182                        },
9183                        write: |settings_content, value| {
9184                            language_settings_field_mut(
9185                                settings_content,
9186                                value,
9187                                |language, value| {
9188                                    language.prettier.get_or_insert_default().options = value;
9189                                },
9190                            )
9191                        },
9192                    }
9193                    .unimplemented(),
9194                ),
9195                metadata: None,
9196                files: USER | PROJECT,
9197            }),
9198        ]
9199    }
9200
9201    concat_sections!(
9202        lsp_section(),
9203        lsp_completions_section(),
9204        debugger_section(),
9205        prettier_section(),
9206    )
9207}
9208
9209fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
9210    [
9211        SettingsPageItem::SectionHeader("Edit Predictions"),
9212        SettingsPageItem::SubPageLink(SubPageLink {
9213            title: "Configure Providers".into(),
9214            r#type: Default::default(),
9215            json_path: Some("edit_predictions.providers"),
9216            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
9217            in_json: false,
9218            files: USER,
9219            render: render_edit_prediction_setup_page
9220        }),
9221        SettingsPageItem::SettingItem(SettingItem {
9222            title: "Show Edit Predictions",
9223            description: "Controls whether edit predictions are shown immediately or manually.",
9224            field: Box::new(SettingField {
9225                json_path: Some("languages.$(language).show_edit_predictions"),
9226                pick: |settings_content| {
9227                    language_settings_field(settings_content, |language| {
9228                        language.show_edit_predictions.as_ref()
9229                    })
9230                },
9231                write: |settings_content, value| {
9232                    language_settings_field_mut(settings_content, value, |language, value| {
9233                        language.show_edit_predictions = value;
9234                    })
9235                },
9236            }),
9237            metadata: None,
9238            files: USER | PROJECT,
9239        }),
9240        SettingsPageItem::SettingItem(SettingItem {
9241            title: "Disable in Language Scopes",
9242            description: "Controls whether edit predictions are shown in the given language scopes.",
9243            field: Box::new(
9244                SettingField {
9245                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
9246                    pick: |settings_content| {
9247                        language_settings_field(settings_content, |language| {
9248                            language.edit_predictions_disabled_in.as_ref()
9249                        })
9250                    },
9251                    write: |settings_content, value| {
9252                        language_settings_field_mut(settings_content, value, |language, value| {
9253                            language.edit_predictions_disabled_in = value;
9254                        })
9255                    },
9256                }
9257                .unimplemented(),
9258            ),
9259            metadata: None,
9260            files: USER | PROJECT,
9261        }),
9262    ]
9263}
9264
9265fn show_scrollbar_or_editor(
9266    settings_content: &SettingsContent,
9267    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
9268) -> Option<&settings::ShowScrollbar> {
9269    show(settings_content).or(settings_content
9270        .editor
9271        .scrollbar
9272        .as_ref()
9273        .and_then(|scrollbar| scrollbar.show.as_ref()))
9274}
9275
9276fn dynamic_variants<T>() -> &'static [T::Discriminant]
9277where
9278    T: strum::IntoDiscriminant,
9279    T::Discriminant: strum::VariantArray,
9280{
9281    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
9282}
9283
9284/// Updates the `vim_mode` setting, disabling `helix_mode` if present and
9285/// `vim_mode` is being enabled.
9286fn write_vim_mode(settings: &mut SettingsContent, value: Option<bool>) {
9287    if value == Some(true) && settings.helix_mode == Some(true) {
9288        settings.helix_mode = Some(false);
9289    }
9290    settings.vim_mode = value;
9291}
9292
9293/// Updates the `helix_mode` setting, disabling `vim_mode` if present and
9294/// `helix_mode` is being enabled.
9295fn write_helix_mode(settings: &mut SettingsContent, value: Option<bool>) {
9296    if value == Some(true) && settings.vim_mode == Some(true) {
9297        settings.vim_mode = Some(false);
9298    }
9299    settings.helix_mode = value;
9300}
9301
9302#[cfg(test)]
9303mod tests {
9304    use super::*;
9305
9306    #[test]
9307    fn test_write_vim_helix_mode() {
9308        // Enabling vim mode while `vim_mode` and `helix_mode` are not yet set
9309        // should only update the `vim_mode` setting.
9310        let mut settings = SettingsContent::default();
9311        write_vim_mode(&mut settings, Some(true));
9312        assert_eq!(settings.vim_mode, Some(true));
9313        assert_eq!(settings.helix_mode, None);
9314
9315        // Enabling helix mode while `vim_mode` and `helix_mode` are not yet set
9316        // should only update the `helix_mode` setting.
9317        let mut settings = SettingsContent::default();
9318        write_helix_mode(&mut settings, Some(true));
9319        assert_eq!(settings.helix_mode, Some(true));
9320        assert_eq!(settings.vim_mode, None);
9321
9322        // Disabling helix mode should only touch `helix_mode` setting when
9323        // `vim_mode` is not set.
9324        write_helix_mode(&mut settings, Some(false));
9325        assert_eq!(settings.helix_mode, Some(false));
9326        assert_eq!(settings.vim_mode, None);
9327
9328        // Enabling vim mode should update `vim_mode` but leave `helix_mode`
9329        // untouched.
9330        write_vim_mode(&mut settings, Some(true));
9331        assert_eq!(settings.vim_mode, Some(true));
9332        assert_eq!(settings.helix_mode, Some(false));
9333
9334        // Enabling helix mode should update `helix_mode` and disable
9335        // `vim_mode`.
9336        write_helix_mode(&mut settings, Some(true));
9337        assert_eq!(settings.helix_mode, Some(true));
9338        assert_eq!(settings.vim_mode, Some(false));
9339
9340        // Enabling vim mode should update `vim_mode` and disable
9341        // `helix_mode`.
9342        write_vim_mode(&mut settings, Some(true));
9343        assert_eq!(settings.vim_mode, Some(true));
9344        assert_eq!(settings.helix_mode, Some(false));
9345    }
9346}