page_data.rs

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