Vim

Conrad Irwin created

Change summary

Cargo.lock                                        |  22 
Cargo.toml                                        |   1 
crates/settings/src/settings_content.rs           |   6 
crates/settings_ui/Cargo.toml                     |  42 
crates/settings_ui/LICENSE-GPL                    |   1 
crates/settings_ui/src/settings_ui.rs             | 940 -----------------
crates/vim/src/digraph.rs                         |   1 
crates/vim/src/normal.rs                          |   3 
crates/vim/src/normal/paste.rs                    |  10 
crates/vim/src/normal/scroll.rs                   |   2 
crates/vim/src/normal/search.rs                   |   1 
crates/vim/src/test.rs                            |   5 
crates/vim/src/test/neovim_backed_test_context.rs |  13 
crates/vim/src/test/vim_test_context.rs           |  10 
crates/vim/src/vim.rs                             |  57 
crates/zed/Cargo.toml                             |   2 
crates/zed/src/main.rs                            |   1 
crates/zed/src/zed.rs                             |  48 
crates/zeta/src/init.rs                           |   2 
crates/zeta_cli/src/headless.rs                   |   2 
20 files changed, 76 insertions(+), 1,093 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -14925,27 +14925,6 @@ dependencies = [
  "zed_actions",
 ]
 
-[[package]]
-name = "settings_ui"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "command_palette_hooks",
- "debugger_ui",
- "editor",
- "feature_flags",
- "gpui",
- "menu",
- "serde",
- "serde_json",
- "settings",
- "smallvec",
- "theme",
- "ui",
- "workspace",
- "workspace-hack",
-]
-
 [[package]]
 name = "settings_ui_macros"
 version = "0.1.0"
@@ -20625,7 +20604,6 @@ dependencies = [
  "session",
  "settings",
  "settings_profile_selector",
- "settings_ui",
  "shellexpand 2.1.2",
  "smol",
  "snippet_provider",

Cargo.toml 🔗

@@ -148,7 +148,6 @@ members = [
     "crates/session",
     "crates/settings",
     "crates/settings_profile_selector",
-    "crates/settings_ui",
     "crates/settings_ui_macros",
     "crates/snippet",
     "crates/snippet_provider",

crates/settings/src/settings_content.rs 🔗

@@ -552,16 +552,12 @@ pub struct VimSettingsContent {
     pub cursor_shape: Option<CursorShapeSettings>,
 }
 
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Debug)]
+#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Debug)]
 #[serde(rename_all = "snake_case")]
 pub enum ModeContent {
     #[default]
     Normal,
     Insert,
-    Replace,
-    Visual,
-    VisualLine,
-    VisualBlock,
     HelixNormal,
 }
 

crates/settings_ui/Cargo.toml 🔗

@@ -1,42 +0,0 @@
-[package]
-name = "settings_ui"
-version = "0.1.0"
-edition.workspace = true
-publish.workspace = true
-license = "GPL-3.0-or-later"
-
-[lints]
-workspace = true
-
-[lib]
-path = "src/settings_ui.rs"
-
-[features]
-default = []
-test-support = []
-
-[dependencies]
-anyhow.workspace = true
-command_palette_hooks.workspace = true
-editor.workspace = true
-feature_flags.workspace = true
-gpui.workspace = true
-menu.workspace = true
-serde.workspace = true
-serde_json.workspace = true
-settings.workspace = true
-smallvec.workspace = true
-theme.workspace = true
-ui.workspace = true
-workspace.workspace = true
-workspace-hack.workspace = true
-
-
-[dev-dependencies]
-debugger_ui.workspace = true
-
-# Uncomment other workspace dependencies as needed
-# assistant.workspace = true
-# client.workspace = true
-# project.workspace = true
-# settings.workspace = true

crates/settings_ui/src/settings_ui.rs 🔗

@@ -1,940 +0,0 @@
-use std::{
-    num::NonZeroU32,
-    ops::{Not, Range},
-    rc::Rc,
-};
-
-use anyhow::Context as _;
-use editor::Editor;
-use feature_flags::{FeatureFlag, FeatureFlagAppExt};
-use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, ReadGlobal, ScrollHandle, actions};
-use settings::{
-    NumType, SettingsStore, SettingsUiEntry, SettingsUiEntryMetaData, SettingsUiItem,
-    SettingsUiItemDynamicMap, SettingsUiItemGroup, SettingsUiItemSingle, SettingsUiItemUnion,
-    SettingsValue,
-};
-use smallvec::SmallVec;
-use ui::{
-    ContextMenu, DropdownMenu, NumericStepper, SwitchField, ToggleButtonGroup, ToggleButtonSimple,
-    prelude::*,
-};
-use workspace::{
-    Workspace,
-    item::{Item, ItemEvent},
-};
-
-pub struct SettingsUiFeatureFlag;
-
-impl FeatureFlag for SettingsUiFeatureFlag {
-    const NAME: &'static str = "settings-ui";
-}
-
-actions!(
-    zed,
-    [
-        /// Opens settings UI.
-        OpenSettingsUi
-    ]
-);
-
-pub fn open_settings_editor(
-    workspace: &mut Workspace,
-    _: &OpenSettingsUi,
-    window: &mut Window,
-    cx: &mut Context<Workspace>,
-) {
-    // todo(settings_ui) open in a local workspace if this is remote.
-    let existing = workspace
-        .active_pane()
-        .read(cx)
-        .items()
-        .find_map(|item| item.downcast::<SettingsPage>());
-
-    if let Some(existing) = existing {
-        workspace.activate_item(&existing, true, true, window, cx);
-    } else {
-        let settings_page = SettingsPage::new(workspace, cx);
-        workspace.add_item_to_active_pane(Box::new(settings_page), None, true, window, cx)
-    }
-}
-
-pub fn init(cx: &mut App) {
-    cx.observe_new(|workspace: &mut Workspace, _, _| {
-        workspace.register_action_renderer(|div, _, _, cx| {
-            let settings_ui_actions = [std::any::TypeId::of::<OpenSettingsUi>()];
-            let has_flag = cx.has_flag::<SettingsUiFeatureFlag>();
-            command_palette_hooks::CommandPaletteFilter::update_global(cx, |filter, _| {
-                if has_flag {
-                    filter.show_action_types(&settings_ui_actions);
-                } else {
-                    filter.hide_action_types(&settings_ui_actions);
-                }
-            });
-            if has_flag {
-                div.on_action(cx.listener(open_settings_editor))
-            } else {
-                div
-            }
-        });
-    })
-    .detach();
-}
-
-pub struct SettingsPage {
-    focus_handle: FocusHandle,
-    settings_tree: SettingsUiTree,
-}
-
-impl SettingsPage {
-    pub fn new(_workspace: &Workspace, cx: &mut Context<Workspace>) -> Entity<Self> {
-        cx.new(|cx| Self {
-            focus_handle: cx.focus_handle(),
-            settings_tree: SettingsUiTree::new(cx),
-        })
-    }
-}
-
-impl EventEmitter<ItemEvent> for SettingsPage {}
-
-impl Focusable for SettingsPage {
-    fn focus_handle(&self, _cx: &App) -> FocusHandle {
-        self.focus_handle.clone()
-    }
-}
-
-impl Item for SettingsPage {
-    type Event = ItemEvent;
-
-    fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
-        Some(Icon::new(IconName::Settings))
-    }
-
-    fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
-        "Settings".into()
-    }
-
-    fn show_toolbar(&self) -> bool {
-        false
-    }
-
-    fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
-        f(*event)
-    }
-}
-
-// We want to iterate over the side bar with root groups
-// - this is a loop over top level groups, and if any are expanded, recursively displaying their items
-// - Should be able to get all items from a group (flatten a group)
-// - Should be able to toggle/untoggle groups in UI (at least in sidebar)
-// - Search should be available
-//  - there should be an index of text -> item mappings, for using fuzzy::match
-//   - Do we want to show the parent groups when a item is matched?
-
-struct UiEntry {
-    title: SharedString,
-    path: Option<SharedString>,
-    documentation: Option<SharedString>,
-    _depth: usize,
-    // a
-    //  b     < a descendant range < a total descendant range
-    //    f   |                    |
-    //    g   |                    |
-    //  c     <                    |
-    //    d                        |
-    //    e                        <
-    descendant_range: Range<usize>,
-    total_descendant_range: Range<usize>,
-    next_sibling: Option<usize>,
-    // expanded: bool,
-    render: Option<SettingsUiItemSingle>,
-    dynamic_render: Option<SettingsUiItemUnion>,
-    generate_items: Option<(
-        SettingsUiItem,
-        fn(&serde_json::Value, &App) -> Vec<SettingsUiEntryMetaData>,
-        SmallVec<[SharedString; 1]>,
-    )>,
-}
-
-impl UiEntry {
-    fn first_descendant_index(&self) -> Option<usize> {
-        return self
-            .descendant_range
-            .is_empty()
-            .not()
-            .then_some(self.descendant_range.start);
-    }
-
-    fn nth_descendant_index(&self, tree: &[UiEntry], n: usize) -> Option<usize> {
-        let first_descendant_index = self.first_descendant_index()?;
-        let mut current_index = 0;
-        let mut current_descendant_index = Some(first_descendant_index);
-        while let Some(descendant_index) = current_descendant_index
-            && current_index < n
-        {
-            current_index += 1;
-            current_descendant_index = tree[descendant_index].next_sibling;
-        }
-        current_descendant_index
-    }
-}
-
-pub struct SettingsUiTree {
-    root_entry_indices: Vec<usize>,
-    entries: Vec<UiEntry>,
-    active_entry_index: usize,
-}
-
-fn build_tree_item(
-    tree: &mut Vec<UiEntry>,
-    entry: SettingsUiEntry,
-    depth: usize,
-    prev_index: Option<usize>,
-) {
-    // let tree: HashMap<Path, UiEntry>;
-    let index = tree.len();
-    tree.push(UiEntry {
-        title: entry.title.into(),
-        path: entry.path.map(SharedString::new_static),
-        documentation: entry.documentation.map(SharedString::new_static),
-        _depth: depth,
-        descendant_range: index + 1..index + 1,
-        total_descendant_range: index + 1..index + 1,
-        render: None,
-        next_sibling: None,
-        dynamic_render: None,
-        generate_items: None,
-    });
-    if let Some(prev_index) = prev_index {
-        tree[prev_index].next_sibling = Some(index);
-    }
-    match entry.item {
-        SettingsUiItem::Group(SettingsUiItemGroup { items: group_items }) => {
-            for group_item in group_items {
-                let prev_index = tree[index]
-                    .descendant_range
-                    .is_empty()
-                    .not()
-                    .then_some(tree[index].descendant_range.end - 1);
-                tree[index].descendant_range.end = tree.len() + 1;
-                build_tree_item(tree, group_item, depth + 1, prev_index);
-                tree[index].total_descendant_range.end = tree.len();
-            }
-        }
-        SettingsUiItem::Single(item) => {
-            tree[index].render = Some(item);
-        }
-        SettingsUiItem::Union(dynamic_render) => {
-            // todo(settings_ui) take from item and store other fields instead of clone
-            // will also require replacing usage in render_recursive so it can know
-            // which options were actually rendered
-            let options = dynamic_render.options.clone();
-            tree[index].dynamic_render = Some(dynamic_render);
-            for option in options {
-                let Some(option) = option else { continue };
-                let prev_index = tree[index]
-                    .descendant_range
-                    .is_empty()
-                    .not()
-                    .then_some(tree[index].descendant_range.end - 1);
-                tree[index].descendant_range.end = tree.len() + 1;
-                build_tree_item(tree, option, depth + 1, prev_index);
-                tree[index].total_descendant_range.end = tree.len();
-            }
-        }
-        SettingsUiItem::DynamicMap(SettingsUiItemDynamicMap {
-            item: generate_settings_ui_item,
-            determine_items,
-            defaults_path,
-        }) => {
-            tree[index].generate_items = Some((
-                generate_settings_ui_item(),
-                determine_items,
-                defaults_path
-                    .into_iter()
-                    .copied()
-                    .map(SharedString::new_static)
-                    .collect(),
-            ));
-        }
-        SettingsUiItem::None => {
-            return;
-        }
-    }
-}
-
-impl SettingsUiTree {
-    pub fn new(cx: &App) -> Self {
-        let settings_store = SettingsStore::global(cx);
-        let mut tree = vec![];
-        let mut root_entry_indices = vec![];
-        for item in settings_store.settings_ui_items() {
-            if matches!(item.item, SettingsUiItem::None)
-            // todo(settings_ui): How to handle top level single items? BaseKeymap is in this category. Probably need a way to
-            // link them to other groups
-            || matches!(item.item, SettingsUiItem::Single(_))
-            {
-                continue;
-            }
-
-            let prev_root_entry_index = root_entry_indices.last().copied();
-            root_entry_indices.push(tree.len());
-            build_tree_item(&mut tree, item, 0, prev_root_entry_index);
-        }
-
-        root_entry_indices.sort_by_key(|i| &tree[*i].title);
-
-        let active_entry_index = root_entry_indices[0];
-        Self {
-            entries: tree,
-            root_entry_indices,
-            active_entry_index,
-        }
-    }
-
-    // todo(settings_ui): Make sure `Item::None` paths are added to the paths tree,
-    // so that we can keep none/skip and still test in CI that all settings have
-    #[cfg(feature = "test-support")]
-    pub fn all_paths(&self, cx: &App) -> Vec<Vec<SharedString>> {
-        // todo(settings_ui) this needs to be implemented not in terms of JSON anymore
-        Vec::default()
-    }
-}
-
-fn render_nav(tree: &SettingsUiTree, _window: &mut Window, cx: &mut Context<SettingsPage>) -> Div {
-    let mut nav = v_flex().p_4().gap_2();
-    for &index in &tree.root_entry_indices {
-        nav = nav.child(
-            div()
-                .id(index)
-                .on_click(cx.listener(move |settings, _, _, _| {
-                    settings.settings_tree.active_entry_index = index;
-                }))
-                .child(
-                    Label::new(tree.entries[index].title.clone())
-                        .size(LabelSize::Large)
-                        .when(tree.active_entry_index == index, |this| {
-                            this.color(Color::Selected)
-                        }),
-                ),
-        );
-    }
-    nav
-}
-
-fn render_content(
-    tree: &SettingsUiTree,
-    window: &mut Window,
-    cx: &mut Context<SettingsPage>,
-) -> Div {
-    let content = v_flex().size_full().gap_4();
-
-    let mut path = smallvec::smallvec![];
-
-    return render_recursive(
-        &tree.entries,
-        tree.active_entry_index,
-        &mut path,
-        content,
-        &mut None,
-        true,
-        window,
-        cx,
-    );
-}
-
-fn render_recursive(
-    tree: &[UiEntry],
-    index: usize,
-    path: &mut SmallVec<[SharedString; 1]>,
-    mut element: Div,
-    fallback_path: &mut Option<SmallVec<[SharedString; 1]>>,
-    render_next_title: bool,
-    window: &mut Window,
-    cx: &mut App,
-) -> Div {
-    let Some(child) = tree.get(index) else {
-        return element
-            .child(Label::new(SharedString::new_static("No settings found")).color(Color::Error));
-    };
-
-    if render_next_title {
-        element = element.child(Label::new(child.title.clone()).size(LabelSize::Large));
-    }
-
-    // todo(settings_ui): subgroups?
-    let mut pushed_path = false;
-    if let Some(child_path) = child.path.as_ref() {
-        path.push(child_path.clone());
-        if let Some(fallback_path) = fallback_path.as_mut() {
-            fallback_path.push(child_path.clone());
-        }
-        pushed_path = true;
-    }
-    let settings_value = settings_value_from_settings_and_path(
-        path.clone(),
-        fallback_path.as_ref().map(|path| path.as_slice()),
-        child.title.clone(),
-        child.documentation.clone(),
-        // PERF: how to structure this better? There feels like there's a way to avoid the clone
-        // and every value lookup
-        SettingsStore::global(cx).raw_user_settings(),
-        SettingsStore::global(cx).raw_default_settings(),
-    );
-    if let Some(dynamic_render) = child.dynamic_render.as_ref() {
-        let value = settings_value.read();
-        let selected_index = (dynamic_render.determine_option)(value, cx);
-        element = element.child(div().child(render_toggle_button_group_inner(
-            settings_value.title.clone(),
-            dynamic_render.labels,
-            Some(selected_index),
-            {
-                let path = settings_value.path.clone();
-                let defaults = dynamic_render.defaults.clone();
-                move |idx, cx| {
-                    if idx == selected_index {
-                        return;
-                    }
-                    let default = defaults.get(idx).cloned().unwrap_or_default();
-                    SettingsValue::write_value(&path, default, cx);
-                }
-            },
-        )));
-        // we don't add descendants for unit options, so we adjust the selected index
-        // by the number of options we didn't add descendants for, to get the descendant index
-        let selected_descendant_index = selected_index
-            - dynamic_render.options[..selected_index]
-                .iter()
-                .filter(|option| option.is_none())
-                .count();
-        if dynamic_render.options[selected_index].is_some()
-            && let Some(descendant_index) =
-                child.nth_descendant_index(tree, selected_descendant_index)
-        {
-            element = render_recursive(
-                tree,
-                descendant_index,
-                path,
-                element,
-                fallback_path,
-                false,
-                window,
-                cx,
-            );
-        }
-    } else if let Some((settings_ui_item, generate_items, defaults_path)) =
-        child.generate_items.as_ref()
-    {
-        let generated_items = generate_items(settings_value.read(), cx);
-        let mut ui_items = Vec::with_capacity(generated_items.len());
-        for item in generated_items {
-            let settings_ui_entry = SettingsUiEntry {
-                path: None,
-                title: "",
-                documentation: None,
-                item: settings_ui_item.clone(),
-            };
-            let prev_index = if ui_items.is_empty() {
-                None
-            } else {
-                Some(ui_items.len() - 1)
-            };
-            let item_index = ui_items.len();
-            build_tree_item(
-                &mut ui_items,
-                settings_ui_entry,
-                child._depth + 1,
-                prev_index,
-            );
-            if item_index < ui_items.len() {
-                ui_items[item_index].path = None;
-                ui_items[item_index].title = item.title.clone();
-                ui_items[item_index].documentation = item.documentation.clone();
-
-                // push path instead of setting path on ui item so that the path isn't pushed to default_path as well
-                // when we recurse
-                path.push(item.path.clone());
-                element = render_recursive(
-                    &ui_items,
-                    item_index,
-                    path,
-                    element,
-                    &mut Some(defaults_path.clone()),
-                    true,
-                    window,
-                    cx,
-                );
-                path.pop();
-            }
-        }
-    } else if let Some(child_render) = child.render.as_ref() {
-        element = element.child(div().child(render_item_single(
-            settings_value,
-            child_render,
-            window,
-            cx,
-        )));
-    } else if let Some(child_index) = child.first_descendant_index() {
-        let mut index = Some(child_index);
-        while let Some(sub_child_index) = index {
-            element = render_recursive(
-                tree,
-                sub_child_index,
-                path,
-                element,
-                fallback_path,
-                true,
-                window,
-                cx,
-            );
-            index = tree[sub_child_index].next_sibling;
-        }
-    } else {
-        element = element.child(div().child(Label::new("// skipped (for now)").color(Color::Muted)))
-    }
-
-    if pushed_path {
-        path.pop();
-        if let Some(fallback_path) = fallback_path.as_mut() {
-            fallback_path.pop();
-        }
-    }
-    return element;
-}
-
-impl Render for SettingsPage {
-    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        let scroll_handle = window.use_state(cx, |_, _| ScrollHandle::new());
-        div()
-            .grid()
-            .grid_cols(16)
-            .p_4()
-            .bg(cx.theme().colors().editor_background)
-            .size_full()
-            .child(
-                div()
-                    .id("settings-ui-nav")
-                    .col_span(2)
-                    .h_full()
-                    .child(render_nav(&self.settings_tree, window, cx)),
-            )
-            .child(
-                div().col_span(6).h_full().child(
-                    render_content(&self.settings_tree, window, cx)
-                        .id("settings-ui-content")
-                        .track_scroll(scroll_handle.read(cx))
-                        .overflow_y_scroll(),
-                ),
-            )
-    }
-}
-
-fn element_id_from_path(path: &[SharedString]) -> ElementId {
-    if path.len() == 0 {
-        panic!("Path length must not be zero");
-    } else if path.len() == 1 {
-        ElementId::Name(path[0].clone())
-    } else {
-        ElementId::from((
-            ElementId::from(path[path.len() - 2].clone()),
-            path[path.len() - 1].clone(),
-        ))
-    }
-}
-
-fn render_item_single(
-    settings_value: SettingsValue<serde_json::Value>,
-    item: &SettingsUiItemSingle,
-    window: &mut Window,
-    cx: &mut App,
-) -> AnyElement {
-    match item {
-        SettingsUiItemSingle::Custom(_) => div()
-            .child(format!("Item: {}", settings_value.path.join(".")))
-            .into_any_element(),
-        SettingsUiItemSingle::SwitchField => {
-            render_any_item(settings_value, render_switch_field, window, cx)
-        }
-        SettingsUiItemSingle::NumericStepper(num_type) => {
-            render_any_numeric_stepper(settings_value, *num_type, window, cx)
-        }
-        SettingsUiItemSingle::ToggleGroup {
-            variants: values,
-            labels: titles,
-        } => render_toggle_button_group(settings_value, values, titles, window, cx),
-        SettingsUiItemSingle::DropDown { variants, labels } => {
-            render_dropdown(settings_value, variants, labels, window, cx)
-        }
-        SettingsUiItemSingle::TextField => render_text_field(settings_value, window, cx),
-    }
-}
-
-pub fn read_settings_value_from_path<'a>(
-    settings_contents: &'a serde_json::Value,
-    path: &[impl AsRef<str>],
-) -> Option<&'a serde_json::Value> {
-    // todo(settings_ui) make non recursive, and move to `settings` alongside SettingsValue, and add method to SettingsValue to get nested
-    let Some((key, remaining)) = path.split_first() else {
-        return Some(settings_contents);
-    };
-    let Some(value) = settings_contents.get(key.as_ref()) else {
-        return None;
-    };
-
-    read_settings_value_from_path(value, remaining)
-}
-
-fn downcast_any_item<T: serde::de::DeserializeOwned>(
-    settings_value: SettingsValue<serde_json::Value>,
-) -> SettingsValue<T> {
-    let value = settings_value.value.map(|value| {
-        serde_json::from_value::<T>(value.clone())
-            .with_context(|| format!("path: {:?}", settings_value.path.join(".")))
-            .with_context(|| format!("value is not a {}: {}", std::any::type_name::<T>(), value))
-            .unwrap()
-    });
-    // todo(settings_ui) Create test that constructs UI tree, and asserts that all elements have default values
-    let default_value = serde_json::from_value::<T>(settings_value.default_value)
-        .with_context(|| format!("path: {:?}", settings_value.path.join(".")))
-        .with_context(|| format!("value is not a {}", std::any::type_name::<T>()))
-        .unwrap();
-    let deserialized_setting_value = SettingsValue {
-        title: settings_value.title,
-        path: settings_value.path,
-        documentation: settings_value.documentation,
-        value,
-        default_value,
-    };
-    deserialized_setting_value
-}
-
-fn render_any_item<T: serde::de::DeserializeOwned>(
-    settings_value: SettingsValue<serde_json::Value>,
-    render_fn: impl Fn(SettingsValue<T>, &mut Window, &mut App) -> AnyElement + 'static,
-    window: &mut Window,
-    cx: &mut App,
-) -> AnyElement {
-    let deserialized_setting_value = downcast_any_item(settings_value);
-    render_fn(deserialized_setting_value, window, cx)
-}
-
-fn render_any_numeric_stepper(
-    settings_value: SettingsValue<serde_json::Value>,
-    num_type: NumType,
-    window: &mut Window,
-    cx: &mut App,
-) -> AnyElement {
-    match num_type {
-        NumType::U64 => render_numeric_stepper::<u64>(
-            downcast_any_item(settings_value),
-            |n| u64::saturating_sub(n, 1),
-            |n| u64::saturating_add(n, 1),
-            |n| {
-                serde_json::Number::try_from(n)
-                    .context("Failed to convert u64 to serde_json::Number")
-            },
-            window,
-            cx,
-        ),
-        NumType::U32 => render_numeric_stepper::<u32>(
-            downcast_any_item(settings_value),
-            |n| u32::saturating_sub(n, 1),
-            |n| u32::saturating_add(n, 1),
-            |n| {
-                serde_json::Number::try_from(n)
-                    .context("Failed to convert u32 to serde_json::Number")
-            },
-            window,
-            cx,
-        ),
-        NumType::F32 => render_numeric_stepper::<f32>(
-            downcast_any_item(settings_value),
-            |a| a - 1.0,
-            |a| a + 1.0,
-            |n| {
-                serde_json::Number::from_f64(n as f64)
-                    .context("Failed to convert f32 to serde_json::Number")
-            },
-            window,
-            cx,
-        ),
-        NumType::USIZE => render_numeric_stepper::<usize>(
-            downcast_any_item(settings_value),
-            |n| usize::saturating_sub(n, 1),
-            |n| usize::saturating_add(n, 1),
-            |n| {
-                serde_json::Number::try_from(n)
-                    .context("Failed to convert usize to serde_json::Number")
-            },
-            window,
-            cx,
-        ),
-        NumType::U32NONZERO => render_numeric_stepper::<NonZeroU32>(
-            downcast_any_item(settings_value),
-            |a| NonZeroU32::new(u32::saturating_sub(a.get(), 1)).unwrap_or(NonZeroU32::MIN),
-            |a| NonZeroU32::new(u32::saturating_add(a.get(), 1)).unwrap_or(NonZeroU32::MAX),
-            |n| {
-                serde_json::Number::try_from(n.get())
-                    .context("Failed to convert usize to serde_json::Number")
-            },
-            window,
-            cx,
-        ),
-    }
-}
-
-fn render_numeric_stepper<T: serde::de::DeserializeOwned + std::fmt::Display + Copy + 'static>(
-    value: SettingsValue<T>,
-    saturating_sub_1: fn(T) -> T,
-    saturating_add_1: fn(T) -> T,
-    to_serde_number: fn(T) -> anyhow::Result<serde_json::Number>,
-    _window: &mut Window,
-    _cx: &mut App,
-) -> AnyElement {
-    let id = element_id_from_path(&value.path);
-    let path = value.path.clone();
-    let num = *value.read();
-
-    NumericStepper::new(
-        id,
-        num.to_string(),
-        {
-            let path = value.path;
-            move |_, _, cx| {
-                let Some(number) = to_serde_number(saturating_sub_1(num)).ok() else {
-                    return;
-                };
-                let new_value = serde_json::Value::Number(number);
-                SettingsValue::write_value(&path, new_value, cx);
-            }
-        },
-        move |_, _, cx| {
-            let Some(number) = to_serde_number(saturating_add_1(num)).ok() else {
-                return;
-            };
-
-            let new_value = serde_json::Value::Number(number);
-
-            SettingsValue::write_value(&path, new_value, cx);
-        },
-    )
-    .style(ui::NumericStepperStyle::Outlined)
-    .into_any_element()
-}
-
-fn render_switch_field(
-    value: SettingsValue<bool>,
-    _window: &mut Window,
-    _cx: &mut App,
-) -> AnyElement {
-    let id = element_id_from_path(&value.path);
-    let path = value.path.clone();
-    SwitchField::new(
-        id,
-        value.title.clone(),
-        value.documentation.clone(),
-        match value.read() {
-            true => ToggleState::Selected,
-            false => ToggleState::Unselected,
-        },
-        move |toggle_state, _, cx| {
-            let new_value = serde_json::Value::Bool(match toggle_state {
-                ToggleState::Indeterminate => {
-                    return;
-                }
-                ToggleState::Selected => true,
-                ToggleState::Unselected => false,
-            });
-
-            SettingsValue::write_value(&path, new_value, cx);
-        },
-    )
-    .into_any_element()
-}
-
-fn render_text_field(
-    value: SettingsValue<serde_json::Value>,
-    window: &mut Window,
-    cx: &mut App,
-) -> AnyElement {
-    let value = downcast_any_item::<String>(value);
-    let path = value.path.clone();
-    let editor = window.use_state(cx, {
-        let path = path.clone();
-        move |window, cx| {
-            let mut editor = Editor::single_line(window, cx);
-
-            cx.observe_global_in::<SettingsStore>(window, move |editor, window, cx| {
-                let user_settings = SettingsStore::global(cx).raw_user_settings();
-                if let Some(value) = read_settings_value_from_path(&user_settings, &path).cloned()
-                    && let Some(value) = value.as_str()
-                {
-                    editor.set_text(value, window, cx);
-                }
-            })
-            .detach();
-
-            editor.set_text(value.read().clone(), window, cx);
-            editor
-        }
-    });
-
-    let weak_editor = editor.downgrade();
-    let theme_colors = cx.theme().colors();
-
-    div()
-        .child(editor)
-        .bg(theme_colors.editor_background)
-        .border_1()
-        .rounded_lg()
-        .border_color(theme_colors.border)
-        .on_action::<menu::Confirm>({
-            move |_, _, cx| {
-                let new_value = weak_editor.read_with(cx, |editor, cx| editor.text(cx)).ok();
-
-                if let Some(new_value) = new_value {
-                    SettingsValue::write_value(&path, serde_json::Value::String(new_value), cx);
-                }
-            }
-        })
-        .into_any_element()
-}
-
-fn render_toggle_button_group(
-    value: SettingsValue<serde_json::Value>,
-    variants: &'static [&'static str],
-    labels: &'static [&'static str],
-    _: &mut Window,
-    _: &mut App,
-) -> AnyElement {
-    let value = downcast_any_item::<String>(value);
-    let active_value = value.read();
-    let selected_idx = variants.iter().position(|v| v == &active_value);
-
-    return render_toggle_button_group_inner(value.title, labels, selected_idx, {
-        let path = value.path.clone();
-        move |variant_index, cx| {
-            SettingsValue::write_value(
-                &path,
-                serde_json::Value::String(variants[variant_index].to_string()),
-                cx,
-            );
-        }
-    });
-}
-
-fn render_dropdown(
-    value: SettingsValue<serde_json::Value>,
-    variants: &'static [&'static str],
-    labels: &'static [&'static str],
-    window: &mut Window,
-    cx: &mut App,
-) -> AnyElement {
-    let value = downcast_any_item::<String>(value);
-    let id = element_id_from_path(&value.path);
-
-    let menu = window.use_keyed_state(id.clone(), cx, |window, cx| {
-        let path = value.path.clone();
-        let handler = Rc::new(move |variant: &'static str, cx: &mut App| {
-            SettingsValue::write_value(&path, serde_json::Value::String(variant.to_string()), cx);
-        });
-
-        ContextMenu::build(window, cx, |mut menu, _, _| {
-            for (label, variant) in labels.iter().zip(variants) {
-                menu = menu.entry(*label, None, {
-                    let handler = handler.clone();
-                    move |_, cx| {
-                        handler(variant, cx);
-                    }
-                });
-            }
-
-            menu
-        })
-    });
-
-    DropdownMenu::new(id, value.read(), menu.read(cx).clone())
-        .style(ui::DropdownStyle::Outlined)
-        .into_any_element()
-}
-
-fn render_toggle_button_group_inner(
-    title: SharedString,
-    labels: &'static [&'static str],
-    selected_idx: Option<usize>,
-    on_write: impl Fn(usize, &mut App) + 'static,
-) -> AnyElement {
-    fn make_toggle_group<const LEN: usize>(
-        title: SharedString,
-        selected_idx: Option<usize>,
-        on_write: Rc<dyn Fn(usize, &mut App)>,
-        labels: &'static [&'static str],
-    ) -> AnyElement {
-        let labels_array: [&'static str; LEN] = {
-            let mut arr = ["unused"; LEN];
-            arr.copy_from_slice(labels);
-            arr
-        };
-
-        let mut idx = 0;
-        ToggleButtonGroup::single_row(
-            title,
-            labels_array.map(|label| {
-                idx += 1;
-                let on_write = on_write.clone();
-                ToggleButtonSimple::new(label, move |_, _, cx| {
-                    on_write(idx - 1, cx);
-                })
-            }),
-        )
-        .when_some(selected_idx, |this, ix| this.selected_index(ix))
-        .style(ui::ToggleButtonGroupStyle::Filled)
-        .into_any_element()
-    }
-
-    let on_write = Rc::new(on_write);
-
-    macro_rules! templ_toggl_with_const_param {
-        ($len:expr) => {
-            if labels.len() == $len {
-                return make_toggle_group::<$len>(title.clone(), selected_idx, on_write, labels);
-            }
-        };
-    }
-    templ_toggl_with_const_param!(1);
-    templ_toggl_with_const_param!(2);
-    templ_toggl_with_const_param!(3);
-    templ_toggl_with_const_param!(4);
-    templ_toggl_with_const_param!(5);
-    templ_toggl_with_const_param!(6);
-    unreachable!("Too many variants");
-}
-
-fn settings_value_from_settings_and_path(
-    path: SmallVec<[SharedString; 1]>,
-    fallback_path: Option<&[SharedString]>,
-    title: SharedString,
-    documentation: Option<SharedString>,
-    user_settings: &serde_json::Value,
-    default_settings: &serde_json::Value,
-) -> SettingsValue<serde_json::Value> {
-    let default_value = read_settings_value_from_path(default_settings, &path)
-        .or_else(|| {
-            fallback_path.and_then(|fallback_path| {
-                read_settings_value_from_path(default_settings, fallback_path)
-            })
-        })
-        .with_context(|| format!("No default value for item at path {:?}", path.join(".")))
-        .expect("Default value set for item")
-        .clone();
-
-    let value = read_settings_value_from_path(user_settings, &path).cloned();
-    let settings_value = SettingsValue {
-        default_value,
-        value,
-        documentation,
-        path,
-        // todo(settings_ui) is title required inside SettingsValue?
-        title,
-    };
-    return settings_value;
-}

crates/vim/src/digraph.rs 🔗

@@ -224,7 +224,6 @@ mod test {
     use settings::SettingsStore;
 
     use crate::{
-        VimSettings,
         state::Mode,
         test::{NeovimBackedTestContext, VimTestContext},
     };

crates/vim/src/normal.rs 🔗

@@ -989,11 +989,10 @@ impl Vim {
 mod test {
     use gpui::{KeyBinding, TestAppContext, UpdateGlobal};
     use indoc::indoc;
-    use language::language_settings::AllLanguageSettings;
     use settings::SettingsStore;
 
     use crate::{
-        VimSettings, motion,
+        motion,
         state::Mode::{self},
         test::{NeovimBackedTestContext, VimTestContext},
     };

crates/vim/src/normal/paste.rs 🔗

@@ -311,17 +311,13 @@ impl Vim {
 #[cfg(test)]
 mod test {
     use crate::{
-        UseSystemClipboard, VimSettings,
         state::{Mode, Register},
         test::{NeovimBackedTestContext, VimTestContext},
     };
     use gpui::ClipboardItem;
     use indoc::indoc;
-    use language::{
-        LanguageName,
-        language_settings::{AllLanguageSettings, LanguageSettingsContent},
-    };
-    use settings::SettingsStore;
+    use language::{LanguageName, language_settings::LanguageSettingsContent};
+    use settings::{SettingsStore, UseSystemClipboard};
 
     #[gpui::test]
     async fn test_paste(cx: &mut gpui::TestAppContext) {
@@ -712,7 +708,7 @@ mod test {
         cx.update_global(|store: &mut SettingsStore, cx| {
             store.update_user_settings(cx, |settings| {
                 settings.project.all_languages.languages.0.insert(
-                    LanguageName::new("Rust"),
+                    LanguageName::new("Rust").0,
                     LanguageSettingsContent {
                         auto_indent_on_paste: Some(false),
                         ..Default::default()

crates/vim/src/normal/scroll.rs 🔗

@@ -271,7 +271,7 @@ mod test {
         state::Mode,
         test::{NeovimBackedTestContext, VimTestContext},
     };
-    use editor::{EditorSettings, ScrollBeyondLastLine};
+    use editor::ScrollBeyondLastLine;
     use gpui::{AppContext as _, point, px, size};
     use indoc::indoc;
     use language::Point;

crates/vim/src/normal/search.rs 🔗

@@ -645,7 +645,6 @@ mod test {
         state::Mode,
         test::{NeovimBackedTestContext, VimTestContext},
     };
-    use editor::EditorSettings;
     use editor::{DisplayPoint, display_map::DisplayRow};
 
     use indoc::indoc;

crates/vim/src/test.rs 🔗

@@ -22,7 +22,6 @@ pub use vim_test_context::*;
 
 use indoc::indoc;
 use search::BufferSearchBar;
-use workspace::WorkspaceSettings;
 
 use crate::{PushSneak, PushSneakBackward, insert::NormalBefore, motion, state::Mode};
 
@@ -1562,10 +1561,10 @@ async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
 async fn test_command_alias(cx: &mut gpui::TestAppContext) {
     let mut cx = VimTestContext::new(cx, true).await;
     cx.update_global(|store: &mut SettingsStore, cx| {
-        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
+        store.update_user_settings(cx, |s| {
             let mut aliases = HashMap::default();
             aliases.insert("Q".to_string(), "upper".to_string());
-            s.command_aliases = Some(aliases)
+            s.workspace.command_aliases = aliases
         });
     });
 

crates/vim/src/test/neovim_backed_test_context.rs 🔗

@@ -6,7 +6,7 @@ use std::{
     panic, thread,
 };
 
-use language::language_settings::{AllLanguageSettings, SoftWrap};
+use language::language_settings::SoftWrap;
 use util::test::marked_text_offsets;
 
 use super::{VimTestContext, neovim_connection::NeovimConnection};
@@ -245,9 +245,14 @@ impl NeovimBackedTestContext {
 
         self.update(|_, cx| {
             SettingsStore::update_global(cx, |settings, cx| {
-                settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-                    settings.defaults.soft_wrap = Some(SoftWrap::PreferredLineLength);
-                    settings.defaults.preferred_line_length = Some(columns);
+                settings.update_user_settings(cx, |settings| {
+                    settings.project.all_languages.defaults.soft_wrap =
+                        Some(SoftWrap::PreferredLineLength);
+                    settings
+                        .project
+                        .all_languages
+                        .defaults
+                        .preferred_line_length = Some(columns);
                 });
             })
         })

crates/vim/src/test/vim_test_context.rs 🔗

@@ -68,7 +68,7 @@ impl VimTestContext {
 
     pub fn init_keybindings(enabled: bool, cx: &mut App) {
         SettingsStore::update_global(cx, |store, cx| {
-            store.update_user_settings::<VimModeSetting>(cx, |s| s.vim_mode = Some(enabled));
+            store.update_user_settings(cx, |s| s.vim_mode = Some(enabled));
         });
         let mut default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
             "keymaps/default-macos.json",
@@ -137,7 +137,7 @@ impl VimTestContext {
     pub fn enable_vim(&mut self) {
         self.cx.update(|_, cx| {
             SettingsStore::update_global(cx, |store, cx| {
-                store.update_user_settings::<VimModeSetting>(cx, |s| s.vim_mode = Some(true));
+                store.update_user_settings(cx, |s| s.vim_mode = Some(true));
             });
         })
     }
@@ -145,7 +145,7 @@ impl VimTestContext {
     pub fn disable_vim(&mut self) {
         self.cx.update(|_, cx| {
             SettingsStore::update_global(cx, |store, cx| {
-                store.update_user_settings::<VimModeSetting>(cx, |s| s.vim_mode = Some(false));
+                store.update_user_settings(cx, |s| s.vim_mode = Some(false));
             });
         })
     }
@@ -153,9 +153,7 @@ impl VimTestContext {
     pub fn enable_helix(&mut self) {
         self.cx.update(|_, cx| {
             SettingsStore::update_global(cx, |store, cx| {
-                store.update_user_settings::<vim_mode_setting::HelixModeSetting>(cx, |s| {
-                    s.helix_mode = Some(true)
-                });
+                store.update_user_settings(cx, |s| s.helix_mode = Some(true));
             });
         })
     }

crates/vim/src/vim.rs 🔗

@@ -19,7 +19,6 @@ mod state;
 mod surrounds;
 mod visual;
 
-use anyhow::Result;
 use collections::HashMap;
 use editor::{
     Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, SelectionEffects,
@@ -38,15 +37,15 @@ use normal::search::SearchSubmit;
 use object::Object;
 use schemars::JsonSchema;
 use serde::Deserialize;
-use serde::Serialize;
-use settings::{
-    Settings, SettingsKey, SettingsSources, SettingsStore, SettingsUi, update_settings_file,
+pub use settings::{
+    ModeContent, Settings, SettingsStore, UseSystemClipboard, update_settings_file,
 };
 use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
 use std::{mem, ops::Range, sync::Arc};
 use surrounds::SurroundsType;
 use theme::ThemeSettings;
 use ui::{IntoElement, SharedString, px};
+use util::MergeFrom;
 use vim_mode_setting::HelixModeSetting;
 use vim_mode_setting::VimModeSetting;
 use workspace::{self, Pane, Workspace};
@@ -1793,34 +1792,61 @@ impl Vim {
     }
 }
 
-#[derive(Deserialize)]
 struct VimSettings {
     pub default_mode: Mode,
     pub toggle_relative_line_numbers: bool,
-    pub use_system_clipboard: UseSystemClipboard,
+    pub use_system_clipboard: settings::UseSystemClipboard,
     pub use_smartcase_find: bool,
     pub custom_digraphs: HashMap<String, Arc<str>>,
     pub highlight_on_yank_duration: u64,
     pub cursor_shape: CursorShapeSettings,
 }
 
+/// The settings for cursor shape.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct CursorShapeSettings {
+    /// Cursor shape for the normal mode.
+    ///
+    /// Default: block
+    pub normal: Option<CursorShape>,
+    /// Cursor shape for the replace mode.
+    ///
+    /// Default: underline
+    pub replace: Option<CursorShape>,
+    /// Cursor shape for the visual mode.
+    ///
+    /// Default: block
+    pub visual: Option<CursorShape>,
+    /// Cursor shape for the insert mode.
+    ///
+    /// The default value follows the primary cursor_shape.
+    pub insert: Option<CursorShape>,
+}
+
+impl From<settings::CursorShapeSettings> for CursorShapeSettings {
+    fn from(settings: settings::CursorShapeSettings) -> Self {
+        Self {
+            normal: settings.normal.map(Into::into),
+            replace: settings.replace.map(Into::into),
+            visual: settings.visual.map(Into::into),
+            insert: settings.insert.map(Into::into),
+        }
+    }
+}
+
 impl From<settings::ModeContent> for Mode {
     fn from(mode: ModeContent) -> Self {
         match mode {
             ModeContent::Normal => Self::Normal,
             ModeContent::Insert => Self::Insert,
-            ModeContent::Replace => Self::Replace,
-            ModeContent::Visual => Self::Visual,
-            ModeContent::VisualLine => Self::VisualLine,
-            ModeContent::VisualBlock => Self::VisualBlock,
             ModeContent::HelixNormal => Self::HelixNormal,
         }
     }
 }
 
 impl Settings for VimSettings {
-    fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self {
-        let vim = content.vim.as_ref().unwrap();
+    fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
+        let vim = content.vim.clone().unwrap();
         Self {
             default_mode: vim.default_mode.unwrap().into(),
             toggle_relative_line_numbers: vim.toggle_relative_line_numbers.unwrap(),
@@ -1828,11 +1854,11 @@ impl Settings for VimSettings {
             use_smartcase_find: vim.use_smartcase_find.unwrap(),
             custom_digraphs: vim.custom_digraphs.unwrap(),
             highlight_on_yank_duration: vim.highlight_on_yank_duration.unwrap(),
-            cursor_shape: vim.cursor_shape.unwrap(),
+            cursor_shape: vim.cursor_shape.unwrap().into(),
         }
     }
 
-    fn refine(&mut self, content: &settings::SettingsContent, cx: &mut App) {
+    fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
         let Some(vim) = content.vim.as_ref() else {
             return;
         };
@@ -1846,7 +1872,8 @@ impl Settings for VimSettings {
         self.custom_digraphs.merge_from(&vim.custom_digraphs);
         self.highlight_on_yank_duration
             .merge_from(&vim.highlight_on_yank_duration);
-        self.cursor_shape.merge_from(&vim.cursor_shape);
+        self.cursor_shape
+            .merge_from(&vim.cursor_shape.map(Into::into));
     }
 
     fn import_from_vscode(

crates/zed/Cargo.toml 🔗

@@ -126,7 +126,6 @@ serde.workspace = true
 serde_json.workspace = true
 session.workspace = true
 settings.workspace = true
-settings_ui.workspace = true
 keymap_editor.workspace = true
 shellexpand.workspace = true
 smol.workspace = true
@@ -185,7 +184,6 @@ itertools.workspace = true
 language = { workspace = true, features = ["test-support"] }
 pretty_assertions.workspace = true
 project = { workspace = true, features = ["test-support"] }
-settings_ui = { workspace = true, features = ["test-support"] }
 terminal_view = { workspace = true, features = ["test-support"] }
 tree-sitter-md.workspace = true
 tree-sitter-rust.workspace = true

crates/zed/src/main.rs 🔗

@@ -624,7 +624,6 @@ pub fn main() {
         markdown_preview::init(cx);
         svg_preview::init(cx);
         onboarding::init(cx);
-        settings_ui::init(cx);
         keymap_editor::init(cx);
         extensions_ui::init(cx);
         zeta::init(cx);

crates/zed/src/zed.rs 🔗

@@ -1948,7 +1948,7 @@ mod tests {
     };
     use language::{LanguageMatcher, LanguageRegistry};
     use pretty_assertions::{assert_eq, assert_ne};
-    use project::{Project, ProjectPath, WorktreeSettings, project_settings::ProjectSettings};
+    use project::{Project, ProjectPath};
     use serde_json::json;
     use settings::{SettingsStore, watch_config_file};
     use std::{
@@ -2253,8 +2253,11 @@ mod tests {
 
         cx.update(|cx| {
             SettingsStore::update_global(cx, |store, cx| {
-                store.update_user_settings::<ProjectSettings>(cx, |settings| {
-                    settings.session.restore_unsaved_buffers = false
+                store.update_user_settings(cx, |settings| {
+                    settings
+                        .session
+                        .get_or_insert_default()
+                        .restore_unsaved_buffers = Some(false)
                 });
             });
         });
@@ -2969,8 +2972,8 @@ mod tests {
         let app_state = init_test(cx);
         cx.update(|cx| {
             cx.update_global::<SettingsStore, _>(|store, cx| {
-                store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
-                    project_settings.file_scan_exclusions =
+                store.update_user_settings(cx, |project_settings| {
+                    project_settings.project.worktree.file_scan_exclusions =
                         Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]);
                 });
             });
@@ -4799,8 +4802,9 @@ mod tests {
 
         // 3. Add .zed to file scan exclusions in user settings
         cx.update_global::<SettingsStore, _>(|store, cx| {
-            store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
-                worktree_settings.file_scan_exclusions = Some(vec![".zed".to_string()]);
+            store.update_user_settings(cx, |worktree_settings| {
+                worktree_settings.project.worktree.file_scan_exclusions =
+                    Some(vec![".zed".to_string()]);
             });
         });
 
@@ -4860,34 +4864,4 @@ mod tests {
             "BUG FOUND: Project settings were overwritten when opening via command - original custom content was lost"
         );
     }
-
-    #[gpui::test]
-    fn test_settings_defaults(cx: &mut TestAppContext) {
-        cx.update(|cx| {
-            settings::init(cx);
-            workspace::init_settings(cx);
-            title_bar::init(cx);
-            editor::init_settings(cx);
-            debugger_ui::init(cx);
-        });
-        let default_json =
-            cx.read(|cx| cx.global::<SettingsStore>().raw_default_settings().clone());
-
-        let all_paths = cx.read(|cx| settings_ui::SettingsUiTree::new(cx).all_paths(cx));
-        let mut failures = Vec::new();
-        for path in all_paths {
-            if settings_ui::read_settings_value_from_path(&default_json, &path).is_none() {
-                failures.push(path);
-            }
-        }
-        if !failures.is_empty() {
-            panic!(
-                "No default value found for paths: {:#?}",
-                failures
-                    .into_iter()
-                    .map(|path| path.join("."))
-                    .collect::<Vec<_>>()
-            );
-        }
-    }
 }

crates/zeta/src/init.rs 🔗

@@ -3,7 +3,7 @@ use std::any::{Any, TypeId};
 use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::{FeatureFlagAppExt as _, PredictEditsRateCompletionsFeatureFlag};
 use gpui::actions;
-use language::language_settings::{AllLanguageSettings, EditPredictionProvider};
+use language::language_settings::EditPredictionProvider;
 use project::DisableAiSettings;
 use settings::{Settings, SettingsStore, update_settings_file};
 use ui::App;

crates/zeta_cli/src/headless.rs 🔗

@@ -31,7 +31,7 @@ pub fn init(cx: &mut App) -> ZetaCliAppState {
     release_channel::init(app_version, cx);
     gpui_tokio::init(cx);
 
-    let mut settings_store = SettingsStore::new(cx, settings::default_settings());
+    let settings_store = SettingsStore::new(cx, &settings::default_settings());
     cx.set_global(settings_store);
     client::init_settings(cx);