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