diff --git a/Cargo.lock b/Cargo.lock index 4e1ea7131b42f7d3056561f40bfd3a809c6ecf00..67734a1d2dff27c9404353f16fbc5cef8fc4c4b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4997,8 +4997,11 @@ name = "docs_preprocessor" version = "0.1.0" dependencies = [ "anyhow", + "collections", + "jsonschema", "mdbook", "regex", + "schemars", "serde", "serde_json", "settings", diff --git a/crates/docs_preprocessor/Cargo.toml b/crates/docs_preprocessor/Cargo.toml index 07da23899956822f7577118ae85b6338b4cefae7..87e5110ff52b6ed7c46de153ca2e809a6e9a5692 100644 --- a/crates/docs_preprocessor/Cargo.toml +++ b/crates/docs_preprocessor/Cargo.toml @@ -7,10 +7,13 @@ license = "GPL-3.0-or-later" [dependencies] anyhow.workspace = true +collections.workspace = true +jsonschema.workspace = true # We are specifically pinning this version of mdbook, as later versions introduce issues with double-nested subdirectories. # Ask @maxdeviant about this before bumping. mdbook = "= 0.4.40" regex.workspace = true +schemars.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true diff --git a/crates/docs_preprocessor/src/main.rs b/crates/docs_preprocessor/src/main.rs index 69d9d6602b199c83bd41b5a23a476771f56e4a2a..428e90ab3cff15017429f6fe862462c899826533 100644 --- a/crates/docs_preprocessor/src/main.rs +++ b/crates/docs_preprocessor/src/main.rs @@ -3,7 +3,7 @@ use mdbook::BookItem; use mdbook::book::{Book, Chapter}; use mdbook::preprocess::CmdPreprocessor; use regex::Regex; -use settings::KeymapFile; +use settings::{KeymapFile, SettingsStore}; use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::io::{self, Read}; @@ -22,7 +22,7 @@ static KEYMAP_WINDOWS: LazyLock = LazyLock::new(|| { load_keymap("keymaps/default-windows.json").expect("Failed to load Windows keymap") }); -static ALL_ACTIONS: LazyLock> = LazyLock::new(load_all_actions); +static ALL_ACTIONS: LazyLock = LazyLock::new(load_all_actions); const FRONT_MATTER_COMMENT: &str = ""; @@ -68,7 +68,7 @@ enum PreprocessorError { impl PreprocessorError { fn new_for_not_found_action(action_name: String) -> Self { - for action in &*ALL_ACTIONS { + for action in &ALL_ACTIONS.actions { for alias in &action.deprecated_aliases { if alias == action_name.as_str() { return PreprocessorError::DeprecatedActionUsed { @@ -256,13 +256,14 @@ fn template_and_validate_actions(book: &mut Book, errors: &mut HashSet Option<&ActionDef> { ALL_ACTIONS + .actions .binary_search_by(|action| action.name.as_str().cmp(name)) .ok() - .map(|index| &ALL_ACTIONS[index]) + .map(|index| &ALL_ACTIONS.actions[index]) } fn actions_available() -> bool { - !ALL_ACTIONS.is_empty() + !ALL_ACTIONS.actions.is_empty() } fn is_missing_action(name: &str) -> bool { @@ -290,6 +291,15 @@ fn find_binding(os: &str, action: &str) -> Option { } fn template_and_validate_json_snippets(book: &mut Book, errors: &mut HashSet) { + let settings_schema = SettingsStore::json_schema(&Default::default()); + let settings_validator = jsonschema::validator_for(&settings_schema) + .expect("failed to compile settings JSON schema"); + + let keymap_schema = + keymap_schema_for_actions(&ALL_ACTIONS.actions, &ALL_ACTIONS.schema_definitions); + let keymap_validator = + jsonschema::validator_for(&keymap_schema).expect("failed to compile keymap JSON schema"); + fn for_each_labeled_code_block_mut( book: &mut Book, errors: &mut HashSet, @@ -378,9 +388,15 @@ fn template_and_validate_json_snippets(book: &mut Book, errors: &mut HashSet
(
-                    &snippet_json_fixed,
-                )?;
+                let value =
+                    settings::parse_json_with_comments::(&snippet_json_fixed)?;
+                let validation_errors: Vec = settings_validator
+                    .iter_errors(&value)
+                    .map(|err| err.to_string())
+                    .collect();
+                if !validation_errors.is_empty() {
+                    anyhow::bail!("{}", validation_errors.join("\n"));
+                }
             }
             "keymap" => {
                 if !snippet_json_fixed.starts_with('[') || !snippet_json_fixed.ends_with(']') {
@@ -388,21 +404,14 @@ fn template_and_validate_json_snippets(book: &mut Book, errors: &mut HashSet
(&snippet_json_fixed)?;
+                let validation_errors: Vec = keymap_validator
+                    .iter_errors(&value)
+                    .map(|err| err.to_string())
+                    .collect();
+                if !validation_errors.is_empty() {
+                    anyhow::bail!("{}", validation_errors.join("\n"));
                 }
             }
             "debug" => {
@@ -505,19 +514,30 @@ where
 struct ActionDef {
     name: String,
     human_name: String,
+    #[serde(default)]
+    schema: Option,
     deprecated_aliases: Vec,
+    #[serde(default)]
+    deprecation_message: Option,
     #[serde(rename = "documentation")]
     docs: Option,
 }
 
-fn load_all_actions() -> Vec {
+#[derive(Debug, serde::Deserialize)]
+struct ActionManifest {
+    actions: Vec,
+    #[serde(default)]
+    schema_definitions: serde_json::Map,
+}
+
+fn load_all_actions() -> ActionManifest {
     let asset_path = concat!(env!("CARGO_MANIFEST_DIR"), "/actions.json");
     match std::fs::read_to_string(asset_path) {
         Ok(content) => {
-            let mut actions: Vec =
+            let mut manifest: ActionManifest =
                 serde_json::from_str(&content).expect("Failed to parse actions.json");
-            actions.sort_by(|a, b| a.name.cmp(&b.name));
-            actions
+            manifest.actions.sort_by(|a, b| a.name.cmp(&b.name));
+            manifest
         }
         Err(err) => {
             if std::env::var("CI").is_ok() {
@@ -527,7 +547,10 @@ fn load_all_actions() -> Vec {
                 "Warning: actions.json not found, action validation will be skipped: {}",
                 err
             );
-            Vec::new()
+            ActionManifest {
+                actions: Vec::new(),
+                schema_definitions: serde_json::Map::new(),
+            }
         }
     }
 }
@@ -661,7 +684,7 @@ fn title_regex() -> &'static Regex {
 }
 
 fn generate_big_table_of_actions() -> String {
-    let actions = &*ALL_ACTIONS;
+    let actions = &ALL_ACTIONS.actions;
     let mut output = String::new();
 
     let mut actions_sorted = actions.iter().collect::>();
@@ -710,3 +733,51 @@ fn generate_big_table_of_actions() -> String {
 
     output
 }
+
+fn keymap_schema_for_actions(
+    actions: &[ActionDef],
+    schema_definitions: &serde_json::Map,
+) -> serde_json::Value {
+    let mut generator = KeymapFile::action_schema_generator();
+
+    for (name, definition) in schema_definitions {
+        generator
+            .definitions_mut()
+            .insert(name.clone(), definition.clone());
+    }
+
+    let mut action_schemas = Vec::new();
+    let mut documentation = collections::HashMap::<&str, &str>::default();
+    let mut deprecations = collections::HashMap::<&str, &str>::default();
+    let mut deprecation_messages = collections::HashMap::<&str, &str>::default();
+
+    for action in actions {
+        let schema = action
+            .schema
+            .as_ref()
+            .and_then(|v| serde_json::from_value::(v.clone()).ok());
+        action_schemas.push((action.name.as_str(), schema));
+        if let Some(doc) = &action.docs {
+            documentation.insert(action.name.as_str(), doc.as_str());
+        }
+        if let Some(msg) = &action.deprecation_message {
+            deprecation_messages.insert(action.name.as_str(), msg.as_str());
+        }
+        for alias in &action.deprecated_aliases {
+            deprecations.insert(alias.as_str(), action.name.as_str());
+            let alias_schema = action
+                .schema
+                .as_ref()
+                .and_then(|v| serde_json::from_value::(v.clone()).ok());
+            action_schemas.push((alias.as_str(), alias_schema));
+        }
+    }
+
+    KeymapFile::generate_json_schema(
+        generator,
+        action_schemas,
+        &documentation,
+        &deprecations,
+        &deprecation_messages,
+    )
+}
diff --git a/crates/json_schema_store/src/json_schema_store.rs b/crates/json_schema_store/src/json_schema_store.rs
index 92d41c1b164ed821c36d661fd2389d89f62a1e03..756f64b2fb1bac13fc6d2868989504a3f8241281 100644
--- a/crates/json_schema_store/src/json_schema_store.rs
+++ b/crates/json_schema_store/src/json_schema_store.rs
@@ -330,15 +330,13 @@ async fn resolve_dynamic_schema(
                 let icon_theme_names = icon_theme_names.as_slice();
                 let theme_names = theme_names.as_slice();
 
-                cx.global::().json_schema(
-                    &settings::SettingsJsonSchemaParams {
-                        language_names,
-                        font_names,
-                        theme_names,
-                        icon_theme_names,
-                        lsp_adapter_names: &lsp_adapter_names,
-                    },
-                )
+                settings::SettingsStore::json_schema(&settings::SettingsJsonSchemaParams {
+                    language_names,
+                    font_names,
+                    theme_names,
+                    icon_theme_names,
+                    lsp_adapter_names: &lsp_adapter_names,
+                })
             })
         }
         "project_settings" => {
@@ -348,25 +346,21 @@ async fn resolve_dynamic_schema(
                 .map(|adapter| adapter.name().to_string())
                 .collect::>();
 
-            cx.update(|cx| {
-                let language_names = &languages
-                    .language_names()
-                    .into_iter()
-                    .map(|name| name.to_string())
-                    .collect::>();
+            let language_names = &languages
+                .language_names()
+                .into_iter()
+                .map(|name| name.to_string())
+                .collect::>();
 
-                cx.global::().project_json_schema(
-                    &settings::SettingsJsonSchemaParams {
-                        language_names,
-                        lsp_adapter_names: &lsp_adapter_names,
-                        // These are not allowed in project-specific settings but
-                        // they're still fields required by the
-                        // `SettingsJsonSchemaParams` struct.
-                        font_names: &[],
-                        theme_names: &[],
-                        icon_theme_names: &[],
-                    },
-                )
+            settings::SettingsStore::project_json_schema(&settings::SettingsJsonSchemaParams {
+                language_names,
+                lsp_adapter_names: &lsp_adapter_names,
+                // These are not allowed in project-specific settings but
+                // they're still fields required by the
+                // `SettingsJsonSchemaParams` struct.
+                font_names: &[],
+                theme_names: &[],
+                icon_theme_names: &[],
             })
         }
         "debug_tasks" => {
diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs
index 8dec480489b7df0066f3c780195e4cecd1bf49c4..67f41b14521480e7082f4070db0ed50d4dfdc1fe 100644
--- a/crates/settings/src/keymap_file.rs
+++ b/crates/settings/src/keymap_file.rs
@@ -531,12 +531,12 @@ impl KeymapFile {
         None
     }
 
-    fn generate_json_schema(
+    pub fn generate_json_schema<'a>(
         mut generator: schemars::SchemaGenerator,
-        action_schemas: Vec<(&'static str, Option)>,
-        action_documentation: &HashMap<&'static str, &'static str>,
-        deprecations: &HashMap<&'static str, &'static str>,
-        deprecation_messages: &HashMap<&'static str, &'static str>,
+        action_schemas: Vec<(&'a str, Option)>,
+        action_documentation: &HashMap<&'a str, &'a str>,
+        deprecations: &HashMap<&'a str, &'a str>,
+        deprecation_messages: &HashMap<&'a str, &'a str>,
     ) -> serde_json::Value {
         fn add_deprecation(schema: &mut schemars::Schema, message: String) {
             schema.insert(
diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs
index 524d0f294e9f955bf05be9fc5601bcb6de1d98d0..411f57375a2303e5e2c30e182365f526989891a4 100644
--- a/crates/settings/src/settings_store.rs
+++ b/crates/settings/src/settings_store.rs
@@ -264,6 +264,7 @@ pub trait AnySettingValue: 'static + Send + Sync {
 }
 
 /// Parameters that are used when generating some JSON schemas at runtime.
+#[derive(Default)]
 pub struct SettingsJsonSchemaParams<'a> {
     pub language_names: &'a [String],
     pub font_names: &'a [String],
@@ -1047,13 +1048,15 @@ impl SettingsStore {
             .subschema_for::()
             .to_value();
 
-        replace_subschema::(generator, || {
-            json_schema!({
-                "type": "object",
-                "errorMessage": "No language with this name is installed.",
-                "properties": params.language_names.iter().map(|name| (name.clone(), language_settings_content_ref.clone())).collect::>()
-            })
-        });
+        if !params.language_names.is_empty() {
+            replace_subschema::(generator, || {
+                json_schema!({
+                    "type": "object",
+                    "errorMessage": "No language with this name is installed.",
+                    "properties": params.language_names.iter().map(|name| (name.clone(), language_settings_content_ref.clone())).collect::>()
+                })
+            });
+        }
 
         generator.subschema_for::();
 
@@ -1063,46 +1066,48 @@ impl SettingsStore {
             .expect("LspSettings should be defined")
             .clone();
 
-        replace_subschema::(generator, || {
-            let mut lsp_properties = serde_json::Map::new();
+        if !params.lsp_adapter_names.is_empty() {
+            replace_subschema::(generator, || {
+                let mut lsp_properties = serde_json::Map::new();
 
-            for adapter_name in params.lsp_adapter_names {
-                let mut base_lsp_settings = lsp_settings_definition
-                    .as_object()
-                    .expect("LspSettings should be an object")
-                    .clone();
+                for adapter_name in params.lsp_adapter_names {
+                    let mut base_lsp_settings = lsp_settings_definition
+                        .as_object()
+                        .expect("LspSettings should be an object")
+                        .clone();
 
-                if let Some(properties) = base_lsp_settings.get_mut("properties") {
-                    if let Some(properties_object) = properties.as_object_mut() {
-                        properties_object.insert(
+                    if let Some(properties) = base_lsp_settings.get_mut("properties") {
+                        if let Some(properties_object) = properties.as_object_mut() {
+                            properties_object.insert(
                             "initialization_options".to_string(),
                             serde_json::json!({
                                 "$ref": format!("{LSP_SETTINGS_SCHEMA_URL_PREFIX}{adapter_name}/initialization_options")
                             }),
                         );
-                        properties_object.insert(
+                            properties_object.insert(
                             "settings".to_string(),
                             serde_json::json!({
                                 "$ref": format!("{LSP_SETTINGS_SCHEMA_URL_PREFIX}{adapter_name}/settings")
                             }),
                         );
+                        }
                     }
-                }
 
-                lsp_properties.insert(
-                    adapter_name.clone(),
-                    serde_json::Value::Object(base_lsp_settings),
-                );
-            }
+                    lsp_properties.insert(
+                        adapter_name.clone(),
+                        serde_json::Value::Object(base_lsp_settings),
+                    );
+                }
 
-            json_schema!({
-                "type": "object",
-                "properties": lsp_properties
-            })
-        });
+                json_schema!({
+                    "type": "object",
+                    "properties": lsp_properties
+                })
+            });
+        }
     }
 
-    pub fn json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
+    pub fn json_schema(params: &SettingsJsonSchemaParams) -> Value {
         let mut generator = schemars::generate::SchemaSettings::draft2019_09()
             .with_transform(DefaultDenyUnknownFields)
             .with_transform(AllowTrailingCommas)
@@ -1111,26 +1116,32 @@ impl SettingsStore {
         UserSettingsContent::json_schema(&mut generator);
         Self::configure_schema_generator(&mut generator, params);
 
-        replace_subschema::(&mut generator, || {
-            json_schema!({
-                "type": "string",
-                "enum": params.font_names,
-            })
-        });
+        if !params.font_names.is_empty() {
+            replace_subschema::(&mut generator, || {
+                json_schema!({
+                     "type": "string",
+                     "enum": params.font_names,
+                })
+            });
+        }
 
-        replace_subschema::(&mut generator, || {
-            json_schema!({
-                "type": "string",
-                "enum": params.theme_names,
-            })
-        });
+        if !params.theme_names.is_empty() {
+            replace_subschema::(&mut generator, || {
+                json_schema!({
+                    "type": "string",
+                    "enum": params.theme_names,
+                })
+            });
+        }
 
-        replace_subschema::(&mut generator, || {
-            json_schema!({
-                "type": "string",
-                "enum": params.icon_theme_names,
-            })
-        });
+        if !params.icon_theme_names.is_empty() {
+            replace_subschema::(&mut generator, || {
+                json_schema!({
+                    "type": "string",
+                    "enum": params.icon_theme_names,
+                })
+            });
+        }
 
         generator
             .root_schema_for::()
@@ -1139,7 +1150,7 @@ impl SettingsStore {
 
     /// Generate JSON schema for project settings, including only settings valid
     /// for project-level configurations.
-    pub fn project_json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
+    pub fn project_json_schema(params: &SettingsJsonSchemaParams) -> Value {
         let mut generator = schemars::generate::SchemaSettings::draft2019_09()
             .with_transform(DefaultDenyUnknownFields)
             .with_transform(AllowTrailingCommas)
@@ -2421,9 +2432,9 @@ mod tests {
 
     #[gpui::test]
     fn test_lsp_settings_schema_generation(cx: &mut App) {
-        let store = SettingsStore::test(cx);
+        SettingsStore::test(cx);
 
-        let schema = store.json_schema(&SettingsJsonSchemaParams {
+        let schema = SettingsStore::json_schema(&SettingsJsonSchemaParams {
             language_names: &["Rust".to_string(), "TypeScript".to_string()],
             font_names: &["Zed Mono".to_string()],
             theme_names: &["One Dark".into()],
@@ -2472,9 +2483,9 @@ mod tests {
 
     #[gpui::test]
     fn test_lsp_project_settings_schema_generation(cx: &mut App) {
-        let store = SettingsStore::test(cx);
+        SettingsStore::test(cx);
 
-        let schema = store.project_json_schema(&SettingsJsonSchemaParams {
+        let schema = SettingsStore::project_json_schema(&SettingsJsonSchemaParams {
             language_names: &["Rust".to_string(), "TypeScript".to_string()],
             font_names: &["Zed Mono".to_string()],
             theme_names: &["One Dark".into()],
@@ -2523,7 +2534,7 @@ mod tests {
 
     #[gpui::test]
     fn test_project_json_schema_differs_from_user_schema(cx: &mut App) {
-        let store = SettingsStore::test(cx);
+        SettingsStore::test(cx);
 
         let params = SettingsJsonSchemaParams {
             language_names: &["Rust".to_string()],
@@ -2533,8 +2544,8 @@ mod tests {
             lsp_adapter_names: &["rust-analyzer".to_string()],
         };
 
-        let user_schema = store.json_schema(¶ms);
-        let project_schema = store.project_json_schema(¶ms);
+        let user_schema = SettingsStore::json_schema(¶ms);
+        let project_schema = SettingsStore::project_json_schema(¶ms);
 
         assert_ne!(user_schema, project_schema);
 
diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs
index d2ccf7b04c3a14efdefa2ca6dbfc0db138d48b02..54db5ba88f13e52fde818d174444d441c1727c73 100644
--- a/crates/zed/src/main.rs
+++ b/crates/zed/src/main.rs
@@ -1756,23 +1756,40 @@ fn dump_all_gpui_actions() {
     struct ActionDef {
         name: &'static str,
         human_name: String,
+        schema: Option,
         deprecated_aliases: &'static [&'static str],
+        deprecation_message: Option<&'static str>,
         documentation: Option<&'static str>,
     }
+    let mut generator = settings::KeymapFile::action_schema_generator();
     let mut actions = gpui::generate_list_of_all_registered_actions()
-        .map(|action| ActionDef {
-            name: action.name,
-            human_name: command_palette::humanize_action_name(action.name),
-            deprecated_aliases: action.deprecated_aliases,
-            documentation: action.documentation,
+        .map(|action| {
+            let schema = (action.json_schema)(&mut generator)
+                .map(|s| serde_json::to_value(s).expect("Failed to serialize action schema"));
+            ActionDef {
+                name: action.name,
+                human_name: command_palette::humanize_action_name(action.name),
+                schema,
+                deprecated_aliases: action.deprecated_aliases,
+                deprecation_message: action.deprecation_message,
+                documentation: action.documentation,
+            }
         })
         .collect::>();
 
     actions.sort_by_key(|a| a.name);
 
+    let schema_definitions = serde_json::to_value(generator.definitions())
+        .expect("Failed to serialize schema definitions");
+
+    let output = serde_json::json!({
+        "actions": actions,
+        "schema_definitions": schema_definitions,
+    });
+
     io::Write::write(
         &mut std::io::stdout(),
-        serde_json::to_string_pretty(&actions).unwrap().as_bytes(),
+        serde_json::to_string_pretty(&output).unwrap().as_bytes(),
     )
     .unwrap();
 }
diff --git a/docs/src/ai/agent-settings.md b/docs/src/ai/agent-settings.md
index 511da53c4353616531205762a111847cb9f41f73..af02bd5f2072ee8e709c65d6237168c6d2159e70 100644
--- a/docs/src/ai/agent-settings.md
+++ b/docs/src/ai/agent-settings.md
@@ -242,8 +242,20 @@ Patterns are **case-insensitive** by default. To make a pattern case-sensitive,
 
 ```json [settings]
 {
-  "pattern": "^Makefile$",
-  "case_sensitive": true
+  "agent": {
+    "tool_permissions": {
+      "tools": {
+        "edit_file": {
+          "always_deny": [
+            {
+              "pattern": "^Makefile$",
+              "case_sensitive": true
+            }
+          ]
+        }
+      }
+    }
+  }
 }
 ```
 
diff --git a/docs/src/ai/edit-prediction.md b/docs/src/ai/edit-prediction.md
index 60431f879432c9866ec7892e119d91a2abe9352a..3d0f8c5141a40daa66fc3689deb2d0363acf273a 100644
--- a/docs/src/ai/edit-prediction.md
+++ b/docs/src/ai/edit-prediction.md
@@ -18,9 +18,11 @@ Once signed in, predictions appear as you type.
 You can confirm that Zeta is properly configured either by verifying whether you have the following code in your settings file:
 
 ```json [settings]
-"edit_predictions": {
-  "provider": "zed"
-},
+{
+  "edit_predictions": {
+    "provider": "zed"
+  }
+}
 ```
 
 The Z icon in the status bar also indicates Zeta is active.
@@ -68,7 +70,7 @@ On Linux, `alt-tab` is often used by the window manager for switching windows, s
 
 By default, `tab` is used to accept edit predictions. You can use another keybinding by inserting this in your keymap:
 
-```json [settings]
+```json [keymap]
 {
   "context": "Editor && edit_prediction",
   "bindings": {
@@ -81,7 +83,7 @@ By default, `tab` is used to accept edit predictions. You can use another keybin
 When there's a [conflict with the `tab` key](#edit-predictions-conflict), Zed uses a different key context to accept keybindings (`edit_prediction_conflict`).
 If you want to use a different one, you can insert this in your keymap:
 
-```json [settings]
+```json [keymap]
 {
   "context": "Editor && edit_prediction_conflict",
   "bindings": {
@@ -95,7 +97,7 @@ If your keybinding contains a modifier (`ctrl` in the example above), it will al
 You can also bind this action to keybind without a modifier.
 In that case, Zed will use the default modifier (`alt`) to preview the edit prediction.
 
-```json [settings]
+```json [keymap]
 {
   "context": "Editor && edit_prediction_conflict",
   "bindings": {
@@ -108,7 +110,7 @@ In that case, Zed will use the default modifier (`alt`) to preview the edit pred
 
 To maintain the use of the modifier key for accepting predictions when there is a language server completions menu, but allow `tab` to accept predictions regardless of cursor position, you can specify the context further with `showing_completions`:
 
-```json [settings]
+```json [keymap]
 {
   "context": "Editor && edit_prediction_conflict && !showing_completions",
   "bindings": {
@@ -261,8 +263,8 @@ To not have predictions appear automatically as you type when working with a spe
 
 ```json [settings]
 {
-  "language": {
-    "python": {
+  "languages": {
+    "Python": {
       "show_edit_predictions": false
     }
   }
@@ -286,9 +288,11 @@ To disable edit predictions for specific directories or files, set this in your
 To completely turn off edit prediction across all providers, explicitly set the settings to `none`, like so:
 
 ```json [settings]
-"features": {
-  "edit_prediction_provider": "none"
-},
+{
+  "edit_predictions": {
+    "provider": "none"
+  }
+}
 ```
 
 ## Configuring Other Providers {#other-providers}
@@ -301,8 +305,8 @@ To use GitHub Copilot as your provider, set this in your settings file ([how to
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "copilot"
+  "edit_predictions": {
+    "provider": "copilot"
   }
 }
 ```
@@ -372,8 +376,8 @@ After adding your API key, Mercury Coder will appear in the provider dropdown in
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "mercury"
+  "edit_predictions": {
+    "provider": "mercury"
   }
 }
 ```
@@ -394,8 +398,8 @@ After adding your API key, Codestral will appear in the provider dropdown in the
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "codestral"
+  "edit_predictions": {
+    "provider": "codestral"
   }
 }
 ```
diff --git a/docs/src/ai/external-agents.md b/docs/src/ai/external-agents.md
index ca556476b7ffcc3752258bebf2b722a7e3ceeae9..d3b240df0e3d6f6b9d1d4d9f223c4a5a0daf2024 100644
--- a/docs/src/ai/external-agents.md
+++ b/docs/src/ai/external-agents.md
@@ -88,7 +88,7 @@ If you'd like to bind this to a keyboard shortcut, you can do so by editing your
 [
   {
     "bindings": {
-      "cmd-alt-c": ["agent::NewExternalAgentThread", { "agent": "claude" }]
+      "cmd-alt-c": ["agent::NewExternalAgentThread", { "agent": "claude_code" }]
     }
   }
 ]
diff --git a/docs/src/ai/llm-providers.md b/docs/src/ai/llm-providers.md
index 7048ffdf9c5d4e974342842b09915888d2e5307b..696427ae6981df5ec75907f7ce2957c07d76eb1f 100644
--- a/docs/src/ai/llm-providers.md
+++ b/docs/src/ai/llm-providers.md
@@ -48,7 +48,7 @@ Ensure your credentials have the following permissions set up:
 
 Your IAM policy should look similar to:
 
-```json [settings]
+```json
 {
   "Version": "2012-10-17",
   "Statement": [
@@ -214,7 +214,7 @@ Custom models will be listed in the model dropdown in the Agent Panel.
 
 You can configure a model to use [extended thinking](https://docs.anthropic.com/en/docs/about-claude/models/extended-thinking-models) (if it supports it) by changing the mode in your model's configuration to `thinking`, for example:
 
-```json [settings]
+```json
 {
   "name": "claude-sonnet-4-latest",
   "display_name": "claude-sonnet-4-thinking",
@@ -762,7 +762,7 @@ The Zed agent comes pre-configured with common Grok models. If you wish to use a
 You can use a custom API endpoint for different providers, as long as it's compatible with the provider's API structure.
 To do so, add the following to your settings file ([how to edit](../configuring-zed.md#settings-files)):
 
-```json [settings]
+```json
 {
   "language_models": {
     "some-provider": {
diff --git a/docs/src/ai/tool-permissions.md b/docs/src/ai/tool-permissions.md
index 61f241728f80efcec25ddc5a116ceb511596f4ef..27ee114e343366ab2700580fa92eea010f40966b 100644
--- a/docs/src/ai/tool-permissions.md
+++ b/docs/src/ai/tool-permissions.md
@@ -92,8 +92,20 @@ For example, a tool called `create_issue` on a server called `github` would be `
 
 ```json [settings]
 {
-  "pattern": "your-regex-here",
-  "case_sensitive": false
+  "agent": {
+    "tool_permissions": {
+      "tools": {
+        "edit_file": {
+          "always_allow": [
+            {
+              "pattern": "your-regex-here",
+              "case_sensitive": false
+            }
+          ]
+        }
+      }
+    }
+  }
 }
 ```
 
diff --git a/docs/src/debugger.md b/docs/src/debugger.md
index 04e4ca7271cb536b55a750f4ce93e5115f8ea5db..2a84821cac88097e61e744f41d74abefd21d3b8b 100644
--- a/docs/src/debugger.md
+++ b/docs/src/debugger.md
@@ -255,7 +255,7 @@ The settings for the debugger are grouped under the `debugger` key in `settings.
 
 - Description: Whether the button should be displayed in the debugger toolbar.
 - Default: `true`
-- Setting: `debugger.show_button`
+- Setting: `debugger.button`
 
 **Options**
 
@@ -264,7 +264,7 @@ The settings for the debugger are grouped under the `debugger` key in `settings.
 ```json [settings]
 {
   "debugger": {
-    "show_button": true
+    "button": true
   }
 }
 ```
diff --git a/docs/src/development/windows.md b/docs/src/development/windows.md
index fcaaaeba344cc1ef057dd7366a3d3ac7f05c3a29..38f578b36b8d295cc220e6f2bfb8d5f0f7dac123 100644
--- a/docs/src/development/windows.md
+++ b/docs/src/development/windows.md
@@ -23,7 +23,7 @@ Clone the [Zed repository](https://github.com/zed-industries/zed).
 
 If you cannot compile Zed, make sure a Visual Studio installation includes at least the following components:
 
-```json [settings]
+```json
 {
   "version": "1.0",
   "components": [
@@ -41,7 +41,7 @@ If you cannot compile Zed, make sure a Visual Studio installation includes at le
 
 If you are using Build Tools only, make sure these components are installed:
 
-```json [settings]
+```json
 {
   "version": "1.0",
   "components": [
diff --git a/docs/src/key-bindings.md b/docs/src/key-bindings.md
index 6d75768bc2a11a15f874d59b5193bae0e73879d1..7b449fea05aabd6deba52a687c571f2533f17baf 100644
--- a/docs/src/key-bindings.md
+++ b/docs/src/key-bindings.md
@@ -99,13 +99,15 @@ The keys can be any single Unicode codepoint that your keyboard generates (for e
 
 A few examples:
 
-```json [settings]
- "bindings": {
-   "cmd-k cmd-s": "zed::OpenKeymap", // matches ⌘-k then ⌘-s
-   "space e": "editor::Complete", // type space then e
-   "ç": "editor::Complete", // matches ⌥-c
-   "shift shift": "file_finder::Toggle", // matches pressing and releasing shift twice
- }
+```json [keymap]
+{
+  "bindings": {
+    "cmd-k cmd-s": "zed::OpenKeymap", // matches ⌘-k then ⌘-s
+    "space e": "editor::ShowCompletions", // type space then e
+    "ç": "editor::ShowCompletions", // matches ⌥-c
+    "shift shift": "file_finder::Toggle" // matches pressing and releasing shift twice
+  }
+}
 ```
 
 The `shift-` modifier can only be used in combination with a letter to indicate the uppercase version. For example, `shift-g` matches typing `G`. Although on many keyboards shift is used to type punctuation characters like `(`, the keypress is not considered to be modified, and so `shift-(` does not match.
@@ -289,7 +291,7 @@ If you're on Linux or Windows, you might find yourself wanting to forward key co
 
 For example, `ctrl-n` creates a new tab in Zed on Linux. If you want to send `ctrl-n` to the built-in terminal when it's focused, add the following to your keymap:
 
-```json [settings]
+```json [keymap]
 {
   "context": "Terminal",
   "bindings": {
diff --git a/docs/src/languages/ansible.md b/docs/src/languages/ansible.md
index 13d8acf762b403a4f4b4c6abf46c71d37df5e421..99980a1a1642717d8306cf8d98ce81be33326207 100644
--- a/docs/src/languages/ansible.md
+++ b/docs/src/languages/ansible.md
@@ -76,7 +76,7 @@ If your inventory file is in the YAML format, you can either:
 
 By default, the following default config is passed to the Ansible language server. It conveniently mirrors the defaults set by [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/blob/03bc581e05e81d33808b42b2d7e76d70adb3b595/lua/lspconfig/configs/ansiblels.lua) for the Ansible language server:
 
-```json [settings]
+```json
 {
   "ansible": {
     "ansible": {
diff --git a/docs/src/languages/biome.md b/docs/src/languages/biome.md
index 716987f02618878a2c4d3b511a0f637d1b6cbe6d..484e36501256dbd00998b1e4c3429f71dee83ed2 100644
--- a/docs/src/languages/biome.md
+++ b/docs/src/languages/biome.md
@@ -29,7 +29,7 @@ The Biome extension includes support for the following languages:
 
 By default, the `biome.json` file is required to be in the root of the workspace.
 
-```json [settings]
+```json
 {
   "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json"
 }
diff --git a/docs/src/languages/cpp.md b/docs/src/languages/cpp.md
index c417ae9cfe323686472573078a21e63891aaa0b4..7fad9a52606964f769510aca19c6dc4302835cc6 100644
--- a/docs/src/languages/cpp.md
+++ b/docs/src/languages/cpp.md
@@ -180,7 +180,7 @@ Allows switching between corresponding C++ source files (e.g., `.cpp`) and heade
 by running the command {#action editor::SwitchSourceHeader} from the command palette or by setting
 a keybinding for the `editor::SwitchSourceHeader` action.
 
-```json [settings]
+```json [keymap]
 {
   "context": "Editor",
   "bindings": {
diff --git a/docs/src/languages/go.md b/docs/src/languages/go.md
index 00d7a7ab154258ddb6f2a01e0b88d098540d5307..e55bd2e67acd0b2c661ababc25ba5916810595c3 100644
--- a/docs/src/languages/go.md
+++ b/docs/src/languages/go.md
@@ -46,7 +46,7 @@ If `gopls` is not found you will likely need to add `export PATH="$PATH:$HOME/go
 
 Zed sets the following initialization options for inlay hints:
 
-```json [settings]
+```json
 "hints": {
     "assignVariableTypes": true,
     "compositeLiteralFields": true,
@@ -62,7 +62,7 @@ to make the language server send back inlay hints when Zed has them enabled in t
 
 Use
 
-```json [settings]
+```json
 "lsp": {
     "gopls": {
         "initialization_options": {
diff --git a/docs/src/languages/json.md b/docs/src/languages/json.md
index dc930a08fc314a4c10d590bd2c36721a686226e3..5da6e4c5ae61b1d7f54317bca86c3b6fee61bf82 100644
--- a/docs/src/languages/json.md
+++ b/docs/src/languages/json.md
@@ -21,7 +21,7 @@ If you use files with the `*.jsonc` extension when using `Format Document` or ha
 
 To workaround this behavior you can add the following to your `.prettierrc` configuration file:
 
-```json [settings]
+```json
 {
   "overrides": [
     {
@@ -45,7 +45,7 @@ To specify a schema inline with your JSON files, add a `$schema` top level key l
 
 For example to for a `.luarc.json` for use with [lua-language-server](https://github.com/LuaLS/lua-language-server/):
 
-```json [settings]
+```json
 {
   "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
   "runtime.version": "Lua 5.4"
diff --git a/docs/src/languages/lua.md b/docs/src/languages/lua.md
index 7900524cc54b889884bc794e35572599e5b1087c..27d3f6136345475343d5c1f4f5bd78e9a7cf39f0 100644
--- a/docs/src/languages/lua.md
+++ b/docs/src/languages/lua.md
@@ -14,7 +14,7 @@ Lua support is available through the [Lua extension](https://github.com/zed-exte
 
 To configure LuaLS you can create a `.luarc.json` file in the root of your project.
 
-```json [settings]
+```json
 {
   "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
   "runtime.version": "Lua 5.4",
@@ -60,7 +60,7 @@ cd .. && git clone https://github.com/notpeter/playdate-luacats
 
 Then in your `.luarc.json`:
 
-```json [settings]
+```json
 {
   "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
   "runtime.version": "Lua 5.4",
@@ -96,6 +96,7 @@ To enable [Inlay Hints](../configuring-languages.md#inlay-hints) for LuaLS in Ze
 1. Configure inlay hints in Settings ({#kb zed::OpenSettings}) under Languages > Lua, or add to your settings file:
 
 ```json [settings]
+{
   "languages": {
     "Lua": {
       "inlay_hints": {
@@ -106,6 +107,7 @@ To enable [Inlay Hints](../configuring-languages.md#inlay-hints) for LuaLS in Ze
       }
     }
   }
+}
 ```
 
 2. Add `"hint.enable": true` to your `.luarc.json`.
@@ -116,7 +118,7 @@ To enable [Inlay Hints](../configuring-languages.md#inlay-hints) for LuaLS in Ze
 
 To enable auto-formatting with your LuaLS (provided by [CppCXY/EmmyLuaCodeStyle](https://github.com/CppCXY/EmmyLuaCodeStyle)) make sure you have `"format.enable": true,` in your .luarc.json:
 
-```json [settings]
+```json
 {
   "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
   "format.enable": true
diff --git a/docs/src/languages/python.md b/docs/src/languages/python.md
index ef2160f46e7138fc32e3d0060b44b83f75832f12..d66f52c71cb9295fe9ca94e5890de48cd1275e57 100644
--- a/docs/src/languages/python.md
+++ b/docs/src/languages/python.md
@@ -153,7 +153,7 @@ basedpyright reads project-specific configuration from the `pyrightconfig.json`
 
 Here's an example `pyrightconfig.json` file that configures basedpyright to use the `strict` type-checking mode and not to issue diagnostics for any files in `__pycache__` directories:
 
-```json [settings]
+```json
 {
   "typeCheckingMode": "strict",
   "ignore": ["**/__pycache__"]
diff --git a/docs/src/languages/ruby.md b/docs/src/languages/ruby.md
index 76a1f02532f7133708d72202bd453f263133ece0..6f8fc1c495743519c8fb776ab9f4fe2a3c1feebc 100644
--- a/docs/src/languages/ruby.md
+++ b/docs/src/languages/ruby.md
@@ -410,11 +410,13 @@ Configure formatting in Settings ({#kb zed::OpenSettings}) under Languages > HTM
 
 ```json [settings]
 {
-  "HTML+ERB": {
-    "formatter": {
-      "external": {
-        "command": "erb-formatter",
-        "arguments": ["--stdin-filename", "{buffer_path}"]
+  "languages": {
+    "HTML+ERB": {
+      "formatter": {
+        "external": {
+          "command": "erb-formatter",
+          "arguments": ["--stdin-filename", "{buffer_path}"]
+        }
       }
     }
   }
diff --git a/docs/src/languages/sql.md b/docs/src/languages/sql.md
index 28a617143a2bf33a3ce2c2994f727cf647d2edab..693b5de55e9dd49087d037b819742e5a1ebbf801 100644
--- a/docs/src/languages/sql.md
+++ b/docs/src/languages/sql.md
@@ -49,7 +49,7 @@ You can add this to Zed project settings (`.zed/settings.json`) or via your Zed
 
 Sql-formatter also allows more precise control by providing [sql-formatter configuration options](https://github.com/sql-formatter-org/sql-formatter#configuration-options). To provide these, create a `.sql-formatter.json` file in your project:
 
-```json [settings]
+```json
 {
   "language": "postgresql",
   "tabWidth": 2,
@@ -61,6 +61,7 @@ Sql-formatter also allows more precise control by providing [sql-formatter confi
 When using a `.sql-formatter.json` file you can use a simplified Zed settings configuration:
 
 ```json [settings]
+{
   "languages": {
     "SQL": {
       "formatter": {
@@ -69,5 +70,6 @@ When using a `.sql-formatter.json` file you can use a simplified Zed settings co
         }
       }
     }
-  },
+  }
+}
 ```
diff --git a/docs/src/languages/svelte.md b/docs/src/languages/svelte.md
index 787654367f4456ae77f1fc729cb1655bd107f32d..e7b17337942d53174d286f6381d002891551fad5 100644
--- a/docs/src/languages/svelte.md
+++ b/docs/src/languages/svelte.md
@@ -14,7 +14,7 @@ Svelte support is available through the [Svelte extension](https://github.com/ze
 
 You can modify how certain styles, such as directives and modifiers, appear in attributes:
 
-```json [settings]
+```json
 "syntax": {
   // Styling for directives (e.g., `class:foo` or `on:click`) (the `on` or `class` part of the attribute).
   "attribute.function": {
@@ -31,7 +31,7 @@ You can modify how certain styles, such as directives and modifiers, appear in a
 
 When inlay hints is enabled in Zed, to make the language server send them back, Zed sets the following initialization options:
 
-```json [settings]
+```json
 "inlayHints": {
   "parameterNames": {
     "enabled": "all",
diff --git a/docs/src/languages/tailwindcss.md b/docs/src/languages/tailwindcss.md
index 72e3ca3955e3b2f95455b06b40860e1161c8e4fa..db0eb1b4d255474ed671ab16ba9f6d235372efa6 100644
--- a/docs/src/languages/tailwindcss.md
+++ b/docs/src/languages/tailwindcss.md
@@ -69,7 +69,7 @@ The `tailwindcss-intellisense-css` language server serves as an alternative to t
 
 Zed supports Prettier out of the box, which means that if you have the [Tailwind CSS Prettier plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) installed, adding it to your Prettier configuration will make it work automatically:
 
-```json [settings]
+```json
 // .prettierrc
 {
   "plugins": ["prettier-plugin-tailwindcss"]
diff --git a/docs/src/languages/yaml.md b/docs/src/languages/yaml.md
index ee5861e0d2b58565948cff4dc7de5a5c667b3b85..747fae072b9d125a5e2c64bc32c8c9898e27787a 100644
--- a/docs/src/languages/yaml.md
+++ b/docs/src/languages/yaml.md
@@ -43,7 +43,7 @@ By default, Zed uses Prettier for formatting YAML files.
 
 You can customize the formatting behavior of Prettier. For example to use single-quotes in yaml files add the following to your `.prettierrc` configuration file:
 
-```json [settings]
+```json
 {
   "overrides": [
     {
diff --git a/docs/src/reference/all-settings.md b/docs/src/reference/all-settings.md
index b32d75b9ab5cdc654f13f01f4b08f36b6e03f6b5..2e3915b1cf08c87b57ffe01c4cfe9cfdd4898869 100644
--- a/docs/src/reference/all-settings.md
+++ b/docs/src/reference/all-settings.md
@@ -423,9 +423,11 @@ A font size from `6` to `100` pixels (inclusive)
 - Default:
 
 ```json [settings]
-"centered_layout": {
-  "left_padding": 0.2,
-  "right_padding": 0.2,
+{
+  "centered_layout": {
+    "left_padding": 0.2,
+    "right_padding": 0.2
+  }
 }
 ```
 
@@ -613,13 +615,17 @@ List of `string` values
 1. Don't show edit predictions in comments:
 
 ```json [settings]
-"disabled_in": ["comment"]
+{
+  "edit_predictions_disabled_in": ["comment"]
+}
 ```
 
 2. Don't show edit predictions in strings and comments:
 
 ```json [settings]
-"disabled_in": ["comment", "string"]
+{
+  "edit_predictions_disabled_in": ["comment", "string"]
+}
 ```
 
 3. Only in Go, don't show edit predictions in strings and comments:
@@ -645,25 +651,33 @@ List of `string` values
 1. Don't highlight the current line:
 
 ```json [settings]
-"current_line_highlight": "none"
+{
+  "current_line_highlight": "none"
+}
 ```
 
 2. Highlight the gutter area:
 
 ```json [settings]
-"current_line_highlight": "gutter"
+{
+  "current_line_highlight": "gutter"
+}
 ```
 
 3. Highlight the editor area:
 
 ```json [settings]
-"current_line_highlight": "line"
+{
+  "current_line_highlight": "line"
+}
 ```
 
 4. Highlight the full line:
 
 ```json [settings]
-"current_line_highlight": "all"
+{
+  "current_line_highlight": "all"
+}
 ```
 
 ## Selection Highlight
@@ -699,25 +713,33 @@ List of `string` values
 1. A vertical bar:
 
 ```json [settings]
-"cursor_shape": "bar"
+{
+  "cursor_shape": "bar"
+}
 ```
 
 2. A block that surrounds the following character:
 
 ```json [settings]
-"cursor_shape": "block"
+{
+  "cursor_shape": "block"
+}
 ```
 
 3. An underline / underscore that runs along the following character:
 
 ```json [settings]
-"cursor_shape": "underline"
+{
+  "cursor_shape": "underline"
+}
 ```
 
 4. An box drawn around the following character:
 
 ```json [settings]
-"cursor_shape": "hollow"
+{
+  "cursor_shape": "hollow"
+}
 ```
 
 ## Gutter
@@ -757,19 +779,25 @@ List of `string` values
 1. Never hide the mouse cursor:
 
 ```json [settings]
-"hide_mouse": "never"
+{
+  "hide_mouse": "never"
+}
 ```
 
 2. Hide only when typing:
 
 ```json [settings]
-"hide_mouse": "on_typing"
+{
+  "hide_mouse": "on_typing"
+}
 ```
 
 3. Hide on both typing and cursor movement:
 
 ```json [settings]
-"hide_mouse": "on_typing_and_movement"
+{
+  "hide_mouse": "on_typing_and_movement"
+}
 ```
 
 ## Snippet Sort Order
@@ -783,25 +811,33 @@ List of `string` values
 1. Place snippets at the top of the completion list:
 
 ```json [settings]
-"snippet_sort_order": "top"
+{
+  "snippet_sort_order": "top"
+}
 ```
 
 2. Place snippets normally without any preference:
 
 ```json [settings]
-"snippet_sort_order": "inline"
+{
+  "snippet_sort_order": "inline"
+}
 ```
 
 3. Place snippets at the bottom of the completion list:
 
 ```json [settings]
-"snippet_sort_order": "bottom"
+{
+  "snippet_sort_order": "bottom"
+}
 ```
 
 4. Do not show snippets in the completion list at all:
 
 ```json [settings]
-"snippet_sort_order": "none"
+{
+  "snippet_sort_order": "none"
+}
 ```
 
 ## Editor Scrollbar
@@ -811,19 +847,21 @@ List of `string` values
 - Default:
 
 ```json [settings]
-"scrollbar": {
-  "show": "auto",
-  "cursors": true,
-  "git_diff": true,
-  "search_results": true,
-  "selected_text": true,
-  "selected_symbol": true,
-  "diagnostics": "all",
-  "axes": {
-    "horizontal": true,
-    "vertical": true,
-  },
-},
+{
+  "scrollbar": {
+    "show": "auto",
+    "cursors": true,
+    "git_diff": true,
+    "search_results": true,
+    "selected_text": true,
+    "selected_symbol": true,
+    "diagnostics": "all",
+    "axes": {
+      "horizontal": true,
+      "vertical": true
+    }
+  }
+}
 ```
 
 ### Show Mode
@@ -837,32 +875,40 @@ List of `string` values
 1. Show the scrollbar if there's important information or follow the system's configured behavior:
 
 ```json [settings]
-"scrollbar": {
-  "show": "auto"
+{
+  "scrollbar": {
+    "show": "auto"
+  }
 }
 ```
 
 2. Match the system's configured behavior:
 
 ```json [settings]
-"scrollbar": {
-  "show": "system"
+{
+  "scrollbar": {
+    "show": "system"
+  }
 }
 ```
 
 3. Always show the scrollbar:
 
 ```json [settings]
-"scrollbar": {
-  "show": "always"
+{
+  "scrollbar": {
+    "show": "always"
+  }
 }
 ```
 
 4. Never show the scrollbar:
 
 ```json [settings]
-"scrollbar": {
-  "show": "never"
+{
+  "scrollbar": {
+    "show": "never"
+  }
 }
 ```
 
@@ -940,7 +986,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show_diagnostics": "all"
+  "scrollbar": {
+    "diagnostics": "all"
+  }
 }
 ```
 
@@ -948,7 +996,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show_diagnostics": "off"
+  "scrollbar": {
+    "diagnostics": "none"
+  }
 }
 ```
 
@@ -956,7 +1006,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show_diagnostics": "error"
+  "scrollbar": {
+    "diagnostics": "error"
+  }
 }
 ```
 
@@ -964,7 +1016,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show_diagnostics": "warning"
+  "scrollbar": {
+    "diagnostics": "warning"
+  }
 }
 ```
 
@@ -972,7 +1026,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show_diagnostics": "info"
+  "scrollbar": {
+    "diagnostics": "information"
+  }
 }
 ```
 
@@ -983,11 +1039,13 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 - Default:
 
 ```json [settings]
-"scrollbar": {
-  "axes": {
-    "horizontal": true,
-    "vertical": true,
-  },
+{
+  "scrollbar": {
+    "axes": {
+      "horizontal": true,
+      "vertical": true
+    }
+  }
 }
 ```
 
@@ -1040,7 +1098,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show": "always"
+  "minimap": {
+    "show": "always"
+  }
 }
 ```
 
@@ -1048,7 +1108,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show": "auto"
+  "minimap": {
+    "show": "auto"
+  }
 }
 ```
 
@@ -1056,7 +1118,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "show": "never"
+  "minimap": {
+    "show": "never"
+  }
 }
 ```
 
@@ -1072,7 +1136,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb": "hover"
+  "minimap": {
+    "thumb": "hover"
+  }
 }
 ```
 
@@ -1080,7 +1146,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb": "always"
+  "minimap": {
+    "thumb": "always"
+  }
 }
 ```
 
@@ -1096,7 +1164,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb_border": "full"
+  "minimap": {
+    "thumb_border": "full"
+  }
 }
 ```
 
@@ -1104,7 +1174,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb_border": "left_open"
+  "minimap": {
+    "thumb_border": "left_open"
+  }
 }
 ```
 
@@ -1112,7 +1184,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb_border": "right_open"
+  "minimap": {
+    "thumb_border": "right_open"
+  }
 }
 ```
 
@@ -1120,7 +1194,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb_border": "left_only"
+  "minimap": {
+    "thumb_border": "left_only"
+  }
 }
 ```
 
@@ -1128,7 +1204,9 @@ Diagnostic indicators appear as colored marks showing errors, warnings, and othe
 
 ```json [settings]
 {
-  "thumb_border": "none"
+  "minimap": {
+    "thumb_border": "none"
+  }
 }
 ```
 
@@ -1197,10 +1275,12 @@ or
 - Default:
 
 ```json [settings]
-"tab_bar": {
-  "show": true,
-  "show_nav_history_buttons": true,
-  "show_tab_bar_buttons": true
+{
+  "tab_bar": {
+    "show": true,
+    "show_nav_history_buttons": true,
+    "show_tab_bar_buttons": true
+  }
 }
 ```
 
@@ -1241,14 +1321,16 @@ or
 - Default:
 
 ```json [settings]
-"tabs": {
-  "close_position": "right",
-  "file_icons": false,
-  "git_status": false,
-  "activate_on_close": "history",
-  "show_close_button": "hover",
-  "show_diagnostics": "off"
-},
+{
+  "tabs": {
+    "close_position": "right",
+    "file_icons": false,
+    "git_status": false,
+    "activate_on_close": "history",
+    "show_close_button": "hover",
+    "show_diagnostics": "off"
+  }
+}
 ```
 
 ### Close Position
@@ -1263,7 +1345,9 @@ or
 
 ```json [settings]
 {
-  "close_position": "right"
+  "tabs": {
+    "close_position": "right"
+  }
 }
 ```
 
@@ -1271,7 +1355,9 @@ or
 
 ```json [settings]
 {
-  "close_position": "left"
+  "tabs": {
+    "close_position": "left"
+  }
 }
 ```
 
@@ -1299,7 +1385,9 @@ or
 
 ```json [settings]
 {
-  "activate_on_close": "history"
+  "tabs": {
+    "activate_on_close": "history"
+  }
 }
 ```
 
@@ -1307,7 +1395,9 @@ or
 
 ```json [settings]
 {
-  "activate_on_close": "neighbour"
+  "tabs": {
+    "activate_on_close": "neighbour"
+  }
 }
 ```
 
@@ -1315,7 +1405,9 @@ or
 
 ```json [settings]
 {
-  "activate_on_close": "left_neighbour"
+  "tabs": {
+    "activate_on_close": "left_neighbour"
+  }
 }
 ```
 
@@ -1331,7 +1423,9 @@ or
 
 ```json [settings]
 {
-  "show_close_button": "hover"
+  "tabs": {
+    "show_close_button": "hover"
+  }
 }
 ```
 
@@ -1339,7 +1433,9 @@ or
 
 ```json [settings]
 {
-  "show_close_button": "always"
+  "tabs": {
+    "show_close_button": "always"
+  }
 }
 ```
 
@@ -1347,7 +1443,9 @@ or
 
 ```json [settings]
 {
-  "show_close_button": "hidden"
+  "tabs": {
+    "show_close_button": "hidden"
+  }
 }
 ```
 
@@ -1363,7 +1461,9 @@ or
 
 ```json [settings]
 {
-  "show_diagnostics": "off"
+  "tabs": {
+    "show_diagnostics": "off"
+  }
 }
 ```
 
@@ -1371,7 +1471,9 @@ or
 
 ```json [settings]
 {
-  "show_diagnostics": "errors"
+  "tabs": {
+    "show_diagnostics": "errors"
+  }
 }
 ```
 
@@ -1379,7 +1481,9 @@ or
 
 ```json [settings]
 {
-  "show_diagnostics": "all"
+  "tabs": {
+    "show_diagnostics": "all"
+  }
 }
 ```
 
@@ -1441,9 +1545,11 @@ When trusted, project settings are synchronized automatically, language and MCP
 - Default:
 
 ```json [settings]
-"drag_and_drop_selection": {
-  "enabled": true,
-  "delay": 300
+{
+  "drag_and_drop_selection": {
+    "enabled": true,
+    "delay": 300
+  }
 }
 ```
 
@@ -1454,13 +1560,15 @@ When trusted, project settings are synchronized automatically, language and MCP
 - Default:
 
 ```json [settings]
-"toolbar": {
-  "breadcrumbs": true,
-  "quick_actions": true,
-  "selections_menu": true,
-  "agent_review": true,
-  "code_actions": false
-},
+{
+  "toolbar": {
+    "breadcrumbs": true,
+    "quick_actions": true,
+    "selections_menu": true,
+    "agent_review": true,
+    "code_actions": false
+  }
+}
 ```
 
 **Options**
@@ -1534,11 +1642,13 @@ Positive `integer` value between 1 and 32. Values outside of this range will be
 - Default:
 
 ```json [settings]
-"status_bar": {
-  "active_language_button": true,
-  "cursor_position_button": true,
-  "line_endings_button": false
-},
+{
+  "status_bar": {
+    "active_language_button": true,
+    "cursor_position_button": true,
+    "line_endings_button": false
+  }
+}
 ```
 
 There is an experimental setting that completely hides the status bar. This causes major usability problems (you will be unable to use many of Zed's features), but is provided for those who value screen real-estate above all else.
@@ -1569,11 +1679,13 @@ Some options are passed via `initialization_options` to the language server. The
 For example to pass the `check` option to `rust-analyzer`, use the following configuration:
 
 ```json [settings]
-"lsp": {
-  "rust-analyzer": {
-    "initialization_options": {
-      "check": {
-        "command": "clippy" // rust-analyzer.check.command (default: "check")
+{
+  "lsp": {
+    "rust-analyzer": {
+      "initialization_options": {
+        "check": {
+          "command": "clippy" // rust-analyzer.check.command (default: "check")
+        }
       }
     }
   }
@@ -1583,11 +1695,13 @@ For example to pass the `check` option to `rust-analyzer`, use the following con
 While other options may be changed at a runtime and should be placed under `settings`:
 
 ```json [settings]
-"lsp": {
-  "yaml-language-server": {
-    "settings": {
-      "yaml": {
-        "keyOrdering": true // Enforces alphabetical ordering of keys in maps
+{
+  "lsp": {
+    "yaml-language-server": {
+      "settings": {
+        "yaml": {
+          "keyOrdering": true // Enforces alphabetical ordering of keys in maps
+        }
       }
     }
   }
@@ -1639,8 +1753,8 @@ While other options may be changed at a runtime and should be placed under `sett
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "zed"
+  "edit_predictions": {
+    "provider": "zed"
   }
 }
 ```
@@ -1657,8 +1771,8 @@ While other options may be changed at a runtime and should be placed under `sett
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "zed"
+  "edit_predictions": {
+    "provider": "zed"
   }
 }
 ```
@@ -1667,8 +1781,8 @@ While other options may be changed at a runtime and should be placed under `sett
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "copilot"
+  "edit_predictions": {
+    "provider": "copilot"
   }
 }
 ```
@@ -1677,8 +1791,8 @@ While other options may be changed at a runtime and should be placed under `sett
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "supermaven"
+  "edit_predictions": {
+    "provider": "supermaven"
   }
 }
 ```
@@ -1687,8 +1801,8 @@ While other options may be changed at a runtime and should be placed under `sett
 
 ```json [settings]
 {
-  "features": {
-    "edit_prediction_provider": "none"
+  "edit_predictions": {
+    "provider": "none"
   }
 }
 ```
@@ -1828,17 +1942,19 @@ The result is still `)))` and not `))))))`, which is what it would be by default
 - Default:
 
 ```json [settings]
-"file_scan_exclusions": [
-  "**/.git",
-  "**/.svn",
-  "**/.hg",
-  "**/.jj",
-  "**/CVS",
-  "**/.DS_Store",
-  "**/Thumbs.db",
-  "**/.classpath",
-  "**/.settings"
-],
+{
+  "file_scan_exclusions": [
+    "**/.git",
+    "**/.svn",
+    "**/.hg",
+    "**/.jj",
+    "**/CVS",
+    "**/.DS_Store",
+    "**/Thumbs.db",
+    "**/.classpath",
+    "**/.settings"
+  ]
+}
 ```
 
 Note, specifying `file_scan_exclusions` in settings.json will override the defaults (shown above). If you are looking to exclude additional items you will need to include all the default values in your settings.
@@ -1850,7 +1966,9 @@ Note, specifying `file_scan_exclusions` in settings.json will override the defau
 - Default:
 
 ```json [settings]
-"file_scan_inclusions": [".env*"],
+{
+  "file_scan_inclusions": [".env*"]
+}
 ```
 
 ## File Types
@@ -1860,9 +1978,16 @@ Note, specifying `file_scan_exclusions` in settings.json will override the defau
 - Default:
 
 ```json [settings]
-"file_types": {
-  "JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json"],
-  "Shell Script": [".env.*"]
+{
+  "file_types": {
+    "JSONC": [
+      "**/.zed/**/*.json",
+      "**/zed/**/*.json",
+      "**/Zed/**/*.json",
+      "**/.vscode/**/*.json"
+    ],
+    "Shell Script": [".env.*"]
+  }
 }
 ```
 
@@ -1892,10 +2017,7 @@ To interpret all `.c` files as C++, files called `MyLockFile` as TOML and files
     "include_warnings": true,
     "inline": {
       "enabled": false
-    },
-    "update_with_cursor": false,
-    "primary_only": false,
-    "use_rendered": false
+    }
   }
 }
 ```
@@ -2345,11 +2467,13 @@ Example:
 - Default:
 
 ```json [settings]
-"icon_theme": {
-  "mode": "system",
-  "dark": "Zed (Default)",
-  "light": "Zed (Default)"
-},
+{
+  "icon_theme": {
+    "mode": "system",
+    "dark": "Zed (Default)",
+    "light": "Zed (Default)"
+  }
+}
 ```
 
 ### Mode
@@ -2364,7 +2488,11 @@ Example:
 
 ```json [settings]
 {
-  "mode": "dark"
+  "icon_theme": {
+    "mode": "dark",
+    "dark": "Zed (Default)",
+    "light": "Zed (Default)"
+  }
 }
 ```
 
@@ -2372,7 +2500,11 @@ Example:
 
 ```json [settings]
 {
-  "mode": "light"
+  "icon_theme": {
+    "mode": "light",
+    "dark": "Zed (Default)",
+    "light": "Zed (Default)"
+  }
 }
 ```
 
@@ -2380,7 +2512,11 @@ Example:
 
 ```json [settings]
 {
-  "mode": "system"
+  "icon_theme": {
+    "mode": "system",
+    "dark": "Zed (Default)",
+    "light": "Zed (Default)"
+  }
 }
 ```
 
@@ -2455,15 +2591,17 @@ Run the {#action icon_theme_selector::Toggle} action in the command palette to s
 - Default:
 
 ```json [settings]
-"inlay_hints": {
-  "enabled": false,
-  "show_type_hints": true,
-  "show_parameter_hints": true,
-  "show_other_hints": true,
-  "show_background": false,
-  "edit_debounce_ms": 700,
-  "scroll_debounce_ms": 50,
-  "toggle_on_modifiers_press": null
+{
+  "inlay_hints": {
+    "enabled": false,
+    "show_type_hints": true,
+    "show_parameter_hints": true,
+    "show_other_hints": true,
+    "show_background": false,
+    "edit_debounce_ms": 700,
+    "scroll_debounce_ms": 50,
+    "toggle_on_modifiers_press": null
+  }
 }
 ```
 
@@ -2488,13 +2626,15 @@ Settings-related hint updates are not debounced.
 All possible config values for `toggle_on_modifiers_press` are:
 
 ```json [settings]
-"inlay_hints": {
-  "toggle_on_modifiers_press": {
-    "control": true,
-    "shift": true,
-    "alt": true,
-    "platform": true,
-    "function": true
+{
+  "inlay_hints": {
+    "toggle_on_modifiers_press": {
+      "control": true,
+      "shift": true,
+      "alt": true,
+      "platform": true,
+      "function": true
+    }
   }
 }
 ```
@@ -2508,11 +2648,12 @@ Unspecified values have a `false` value, hints won't be toggled if all the modif
 - Default:
 
 ```json [settings]
-"journal": {
-  "path": "~",
-  "hour_format": "hour12"
+{
+  "journal": {
+    "path": "~",
+    "hour_format": "hour12"
+  }
 }
-
 ```
 
 ### Path
@@ -2537,7 +2678,9 @@ Unspecified values have a `false` value, hints won't be toggled if all the modif
 
 ```json [settings]
 {
-  "hour_format": "hour12"
+  "journal": {
+    "hour_format": "hour12"
+  }
 }
 ```
 
@@ -2545,7 +2688,9 @@ Unspecified values have a `false` value, hints won't be toggled if all the modif
 
 ```json [settings]
 {
-  "hour_format": "hour24"
+  "journal": {
+    "hour_format": "hour24"
+  }
 }
 ```
 
@@ -2578,14 +2723,16 @@ Unspecified values have a `false` value, hints won't be toggled if all the modif
 To override settings for a language, add an entry for that languages name to the `languages` value. Example:
 
 ```json [settings]
-"languages": {
-  "C": {
-    "format_on_save": "off",
-    "preferred_line_length": 64,
-    "soft_wrap": "preferred_line_length"
-  },
-  "JSON": {
-    "tab_size": 4
+{
+  "languages": {
+    "C": {
+      "format_on_save": "off",
+      "preferred_line_length": 64,
+      "soft_wrap": "preferred_line_length"
+    },
+    "JSON": {
+      "tab_size": 4
+    }
   }
 }
 ```
@@ -2855,14 +3002,16 @@ Configuration object for defining settings profiles. Example:
 - Default:
 
 ```json [settings]
-"preview_tabs": {
-  "enabled": true,
-  "enable_preview_from_project_panel": true,
-  "enable_preview_from_file_finder": false,
-  "enable_preview_from_multibuffer": true,
-  "enable_preview_multibuffer_from_code_navigation": false,
-  "enable_preview_file_from_code_navigation": true,
-  "enable_keep_preview_on_code_navigation": false,
+{
+  "preview_tabs": {
+    "enabled": true,
+    "enable_preview_from_project_panel": true,
+    "enable_preview_from_file_finder": false,
+    "enable_preview_from_multibuffer": true,
+    "enable_preview_multibuffer_from_code_navigation": false,
+    "enable_preview_file_from_code_navigation": true,
+    "enable_keep_preview_on_code_navigation": false
+  }
 }
 ```
 
@@ -3233,14 +3382,16 @@ Non-negative `integer` values
 - Default:
 
 ```json [settings]
-"search": {
-  "button": true,
-  "whole_word": false,
-  "case_sensitive": false,
-  "include_ignored": false,
-  "regex": false,
-  "center_on_match": false
-},
+{
+  "search": {
+    "button": true,
+    "whole_word": false,
+    "case_sensitive": false,
+    "include_ignored": false,
+    "regex": false,
+    "center_on_match": false
+  }
+}
 ```
 
 ### Button
@@ -3678,10 +3829,12 @@ List of `integer` column numbers
 - Default:
 
 ```json [settings]
-"telemetry": {
-  "diagnostics": true,
-  "metrics": true
-},
+{
+  "telemetry": {
+    "diagnostics": true,
+    "metrics": true
+  }
+}
 ```
 
 **Options**
@@ -4330,14 +4483,16 @@ Example command to set the title: `echo -e "\e]2;New Title\007";`
 - Default:
 
 ```json [settings]
-"repl": {
-  // Maximum number of columns to keep in REPL's scrollback buffer.
-  // Clamped with [20, 512] range.
-  "max_columns": 128,
-  // Maximum number of lines to keep in REPL's scrollback buffer.
-  // Clamped with [4, 256] range.
-  "max_lines": 32
-},
+{
+  "repl": {
+    // Maximum number of columns to keep in REPL's scrollback buffer.
+    // Clamped with [20, 512] range.
+    "max_columns": 128,
+    // Maximum number of lines to keep in REPL's scrollback buffer.
+    // Clamped with [4, 256] range.
+    "max_lines": 32
+  }
+}
 ```
 
 ## Theme
@@ -4353,11 +4508,13 @@ Example command to set the title: `echo -e "\e]2;New Title\007";`
 - Default:
 
 ```json [settings]
-"theme": {
-  "mode": "system",
-  "dark": "One Dark",
-  "light": "One Light"
-},
+{
+  "theme": {
+    "mode": "system",
+    "dark": "One Dark",
+    "light": "One Light"
+  }
+}
 ```
 
 ### Mode
@@ -4372,7 +4529,11 @@ Example command to set the title: `echo -e "\e]2;New Title\007";`
 
 ```json [settings]
 {
-  "mode": "dark"
+  "theme": {
+    "mode": "dark",
+    "dark": "One Dark",
+    "light": "One Light"
+  }
 }
 ```
 
@@ -4380,7 +4541,11 @@ Example command to set the title: `echo -e "\e]2;New Title\007";`
 
 ```json [settings]
 {
-  "mode": "light"
+  "theme": {
+    "mode": "light",
+    "dark": "One Dark",
+    "light": "One Light"
+  }
 }
 ```
 
@@ -4388,7 +4553,11 @@ Example command to set the title: `echo -e "\e]2;New Title\007";`
 
 ```json [settings]
 {
-  "mode": "system"
+  "theme": {
+    "mode": "system",
+    "dark": "One Dark",
+    "light": "One Light"
+  }
 }
 ```
 
@@ -4419,15 +4588,17 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 - Default:
 
 ```json [settings]
-"title_bar": {
-  "show_branch_icon": false,
-  "show_branch_name": true,
-  "show_project_items": true,
-  "show_onboarding_banner": true,
-  "show_user_picture": true,
-  "show_user_menu": true,
-  "show_sign_in": true,
-  "show_menus": false
+{
+  "title_bar": {
+    "show_branch_icon": false,
+    "show_branch_name": true,
+    "show_project_items": true,
+    "show_onboarding_banner": true,
+    "show_user_picture": true,
+    "show_user_menu": true,
+    "show_sign_in": true,
+    "show_menus": false
+  }
 }
 ```
 
@@ -4534,7 +4705,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "dock": "left"
+  "project_panel": {
+    "dock": "left"
+  }
 }
 ```
 
@@ -4542,7 +4715,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "dock": "right"
+  "project_panel": {
+    "dock": "right"
+  }
 }
 ```
 
@@ -4558,7 +4733,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "entry_spacing": "comfortable"
+  "project_panel": {
+    "entry_spacing": "comfortable"
+  }
 }
 ```
 
@@ -4566,7 +4743,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "entry_spacing": "standard"
+  "project_panel": {
+    "entry_spacing": "standard"
+  }
 }
 ```
 
@@ -4582,7 +4761,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "git_status": true
+  "project_panel": {
+    "git_status": true
+  }
 }
 ```
 
@@ -4590,7 +4771,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "git_status": false
+  "project_panel": {
+    "git_status": false
+  }
 }
 ```
 
@@ -4616,7 +4799,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "auto_reveal_entries": true
+  "project_panel": {
+    "auto_reveal_entries": true
+  }
 }
 ```
 
@@ -4624,7 +4809,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "auto_reveal_entries": false
+  "project_panel": {
+    "auto_reveal_entries": false
+  }
 }
 ```
 
@@ -4640,7 +4827,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "auto_fold_dirs": true
+  "project_panel": {
+    "auto_fold_dirs": true
+  }
 }
 ```
 
@@ -4648,7 +4837,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "auto_fold_dirs": false
+  "project_panel": {
+    "auto_fold_dirs": false
+  }
 }
 ```
 
@@ -4664,7 +4855,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "bold_folder_labels": true
+  "project_panel": {
+    "bold_folder_labels": true
+  }
 }
 ```
 
@@ -4672,7 +4865,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "bold_folder_labels": false
+  "project_panel": {
+    "bold_folder_labels": false
+  }
 }
 ```
 
@@ -4689,8 +4884,12 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 - Default:
 
 ```json [settings]
-"indent_guides": {
-  "show": "always"
+{
+  "project_panel": {
+    "indent_guides": {
+      "show": "always"
+    }
+  }
 }
 ```
 
@@ -4700,8 +4899,10 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "indent_guides": {
-    "show": "always"
+  "project_panel": {
+    "indent_guides": {
+      "show": "always"
+    }
   }
 }
 ```
@@ -4710,8 +4911,10 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "indent_guides": {
-    "show": "never"
+  "project_panel": {
+    "indent_guides": {
+      "show": "never"
+    }
   }
 }
 ```
@@ -4723,8 +4926,12 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 - Default:
 
 ```json [settings]
-"scrollbar": {
-  "show": null
+{
+  "project_panel": {
+    "scrollbar": {
+      "show": null
+    }
+  }
 }
 ```
 
@@ -4734,8 +4941,10 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "scrollbar": {
-    "show": "always"
+  "project_panel": {
+    "scrollbar": {
+      "show": "always"
+    }
   }
 }
 ```
@@ -4744,8 +4953,10 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "scrollbar": {
-    "show": "never"
+  "project_panel": {
+    "scrollbar": {
+      "show": "never"
+    }
   }
 }
 ```
@@ -4762,7 +4973,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "sort_mode": "directories_first"
+  "project_panel": {
+    "sort_mode": "directories_first"
+  }
 }
 ```
 
@@ -4770,7 +4983,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "sort_mode": "mixed"
+  "project_panel": {
+    "sort_mode": "mixed"
+  }
 }
 ```
 
@@ -4778,7 +4993,9 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 
 ```json [settings]
 {
-  "sort_mode": "files_first"
+  "project_panel": {
+    "sort_mode": "files_first"
+  }
 }
 ```
 
@@ -4789,10 +5006,14 @@ Run the {#action theme_selector::Toggle} action in the command palette to see a
 - Default:
 
 ```json [settings]
-"auto_open": {
-  "on_create": true,
-  "on_paste": true,
-  "on_drop": true
+{
+  "project_panel": {
+    "auto_open": {
+      "on_create": true,
+      "on_paste": true,
+      "on_drop": true
+    }
+  }
 }
 ```
 
@@ -4916,21 +5137,23 @@ You can define these in user or project settings; project settings are merged on
 - Default:
 
 ```json [settings]
-"outline_panel": {
-  "button": true,
-  "default_width": 300,
-  "dock": "left",
-  "file_icons": true,
-  "folder_icons": true,
-  "git_status": true,
-  "indent_size": 20,
-  "auto_reveal_entries": true,
-  "auto_fold_dirs": true,
-  "indent_guides": {
-    "show": "always"
-  },
-  "scrollbar": {
-    "show": null
+{
+  "outline_panel": {
+    "button": true,
+    "default_width": 300,
+    "dock": "left",
+    "file_icons": true,
+    "folder_icons": true,
+    "git_status": true,
+    "indent_size": 20,
+    "auto_reveal_entries": true,
+    "auto_fold_dirs": true,
+    "indent_guides": {
+      "show": "always"
+    },
+    "scrollbar": {
+      "show": null
+    }
   }
 }
 ```
@@ -4942,12 +5165,14 @@ You can define these in user or project settings; project settings are merged on
 - Default:
 
 ```json [settings]
-"calls": {
-  // Join calls with the microphone live by default
-  "mute_on_join": false,
-  // Share your project when you are the first to join a channel
-  "share_on_join": false
-},
+{
+  "calls": {
+    // Join calls with the microphone live by default
+    "mute_on_join": false,
+    // Share your project when you are the first to join a channel
+    "share_on_join": false
+  }
+}
 ```
 
 ## Colorize Brackets
@@ -5000,8 +5225,10 @@ The name of any font family installed on the system, `".ZedSans"` to use the Zed
 - Default:
 
 ```json [settings]
-"ui_font_features": {
-  "calt": false
+{
+  "ui_font_features": {
+    "calt": false
+  }
 }
 ```
 
@@ -5080,26 +5307,28 @@ Each key within this object is the name of a settings profile, and each value is
 Example:
 
 ```json [settings]
-"profiles": {
-  "Presenting (Dark)": {
-    "agent_buffer_font_size": 18.0,
-    "buffer_font_size": 18.0,
-    "theme": "One Dark",
-    "ui_font_size": 18.0
-  },
-  "Presenting (Light)": {
-    "agent_buffer_font_size": 18.0,
-    "buffer_font_size": 18.0,
-    "theme": "One Light",
-    "ui_font_size": 18.0
-  },
-  "Writing": {
-    "agent_buffer_font_size": 15.0,
-    "buffer_font_size": 15.0,
-    "theme": "Catppuccin Frappé - No Italics",
-    "ui_font_size": 15.0,
-    "tab_bar": { "show": false },
-    "toolbar": { "breadcrumbs": false }
+{
+  "profiles": {
+    "Presenting (Dark)": {
+      "agent_buffer_font_size": 18.0,
+      "buffer_font_size": 18.0,
+      "theme": "One Dark",
+      "ui_font_size": 18.0
+    },
+    "Presenting (Light)": {
+      "agent_buffer_font_size": 18.0,
+      "buffer_font_size": 18.0,
+      "theme": "One Light",
+      "ui_font_size": 18.0
+    },
+    "Writing": {
+      "agent_buffer_font_size": 15.0,
+      "buffer_font_size": 15.0,
+      "theme": "Catppuccin Frappé - No Italics",
+      "ui_font_size": 15.0,
+      "tab_bar": { "show": false },
+      "toolbar": { "breadcrumbs": false }
+    }
   }
 }
 ```
@@ -5122,7 +5351,6 @@ To preview and enable a settings profile, open the command palette via {#kb comm
   "autosave": "on_focus_change",
   "format_on_save": "off",
   "vim_mode": false,
-  "projects_online_by_default": true,
   "terminal": {
     "font_family": "FiraCode Nerd Font Mono",
     "blinking": "off"
diff --git a/docs/src/snippets.md b/docs/src/snippets.md
index 8d663372b8983801efb2405c17f2f580ebbf6e97..72cbec7b20ff694304a58a70cd9b142a60fc58a2 100644
--- a/docs/src/snippets.md
+++ b/docs/src/snippets.md
@@ -11,7 +11,7 @@ The snippets are located in `~/.config/zed/snippets` directory to which you can
 
 ## Example configuration
 
-```json [settings]
+```json
 {
   // Each snippet must have a name and body, but the prefix and description are optional.
   // The prefix is used to trigger the snippet, but when omitted then the name is used.
diff --git a/docs/src/tasks.md b/docs/src/tasks.md
index ee2ce07601187a59b0987720ebd43197b7d1a3ae..81ab7c97be03ad3d1e86a6fd7995f341dc9cee8b 100644
--- a/docs/src/tasks.md
+++ b/docs/src/tasks.md
@@ -94,7 +94,7 @@ These variables allow you to pull information from the current editor and use it
 
 To use a variable in a task, prefix it with a dollar sign (`$`):
 
-```json [settings]
+```json [tasks]
 {
   "label": "echo current file's path",
   "command": "echo $ZED_FILE"
@@ -111,7 +111,7 @@ When working with paths containing spaces or other special characters, please en
 
 For example, instead of this (which will fail if the path has a space):
 
-```json [settings]
+```json [tasks]
 {
   "label": "stat current file",
   "command": "stat $ZED_FILE"
@@ -120,7 +120,7 @@ For example, instead of this (which will fail if the path has a space):
 
 Provide the following:
 
-```json [settings]
+```json [tasks]
 {
   "label": "stat current file",
   "command": "stat",
@@ -130,7 +130,7 @@ Provide the following:
 
 Or explicitly include escaped quotes like so:
 
-```json [settings]
+```json [tasks]
 {
   "label": "stat current file",
   "command": "stat \"$ZED_FILE\""
@@ -142,7 +142,7 @@ Or explicitly include escaped quotes like so:
 Task definitions with variables which are not present at the moment the task list is determined are filtered out.
 For example, the following task will appear in the spawn modal only if there is a text selection:
 
-```json [settings]
+```json [tasks]
 {
   "label": "selected text",
   "command": "echo \"$ZED_SELECTED_TEXT\""
@@ -151,7 +151,7 @@ For example, the following task will appear in the spawn modal only if there is
 
 Set default values to such variables to have such tasks always displayed:
 
-```json [settings]
+```json [tasks]
 {
   "label": "selected text with default",
   "command": "echo \"${ZED_SELECTED_TEXT:no text selected}\""
@@ -233,7 +233,7 @@ Zed supports overriding the default action for inline runnable indicators via wo
 
 To tag a task, add the runnable tag name to the `tags` field on the task template:
 
-```json [settings]
+```json [tasks]
 {
   "label": "echo current file's path",
   "command": "echo $ZED_FILE",
diff --git a/docs/src/vim.md b/docs/src/vim.md
index f45f0700695cafddf316248c0c84b9440e52a402..41e3345c8c52fd858d7f66edae53af368e04cce3 100644
--- a/docs/src/vim.md
+++ b/docs/src/vim.md
@@ -216,7 +216,7 @@ These text objects implement the behavior of the [mini.ai](https://github.com/ec
 
 To use these text objects, you need to add bindings to your keymap. Here's an example configuration that makes them available when using text object operators (`i` and `a`) or change-surrounds (`cs`):
 
-```json [settings]
+```json [keymap]
 {
   "context": "vim_operator == a || vim_operator == i || vim_operator == cs",
   "bindings": {
@@ -368,11 +368,11 @@ As any Zed command is available, you may find that it's helpful to remember mnem
 
 Zed's key bindings are evaluated only when the `"context"` property matches your location in the editor. For example, if you add key bindings to the `"Editor"` context, they will only work when you're editing a file. If you add key bindings to the `"Workspace"` context, they will work everywhere in Zed. Here's an example of a key binding that saves when you're editing a file:
 
-```json [settings]
+```json [keymap]
 {
   "context": "Editor",
   "bindings": {
-    "ctrl-s": "file::Save"
+    "ctrl-s": "workspace::Save"
   }
 }
 ```
@@ -449,7 +449,7 @@ By default, you can navigate between the different files open in the editor with
 
 But you cannot use the same shortcuts to move between all the editor docks (the terminal, project panel, assistant panel, ...). If you want to use the same shortcuts to navigate to the docks, you can add the following key bindings to your user keymap.
 
-```json [settings]
+```json [keymap]
 {
   "context": "Dock",
   "bindings": {
@@ -464,7 +464,7 @@ But you cannot use the same shortcuts to move between all the editor docks (the
 
 Subword motion, which allows you to navigate and select individual words in `camelCase` or `snake_case`, is not enabled by default. To enable it, add these bindings to your keymap.
 
-```json [settings]
+```json [keymap]
 {
   "context": "VimControl && !menu && vim_mode != operator",
   "bindings": {
@@ -481,7 +481,7 @@ Subword motion, which allows you to navigate and select individual words in `cam
 
 Vim mode comes with shortcuts to surround the selection in normal mode (`ys`), but it doesn't have a shortcut to add surrounds in visual mode. By default, `shift-s` substitutes the selection (erases the text and enters insert mode). To use `shift-s` to add surrounds in visual mode, you can add the following object to your keymap.
 
-```json [settings]
+```json [keymap]
 {
   "context": "vim_mode == visual",
   "bindings": {
@@ -492,7 +492,7 @@ Vim mode comes with shortcuts to surround the selection in normal mode (`ys`), b
 
 In non-modal text editors, cursor navigation typically wraps when moving past line ends. Zed, however, handles this behavior exactly like Vim by default: the cursor stops at line boundaries. If you prefer your cursor to wrap between lines, override these keybindings:
 
-```json [settings]
+```json [keymap]
 // In VimScript, this would look like this:
 // set whichwrap+=<,>,[,],h,l
 {
@@ -508,7 +508,7 @@ In non-modal text editors, cursor navigation typically wraps when moving past li
 
 The [Sneak motion](https://github.com/justinmk/vim-sneak) feature allows for quick navigation to any two-character sequence in your text. You can enable it by adding the following keybindings to your keymap. By default, the `s` key is mapped to `vim::Substitute`. Adding these bindings will override that behavior, so ensure this change aligns with your workflow preferences.
 
-```json [settings]
+```json [keymap]
 {
   "context": "vim_mode == normal || vim_mode == visual",
   "bindings": {
@@ -520,7 +520,7 @@ The [Sneak motion](https://github.com/justinmk/vim-sneak) feature allows for qui
 
 The [vim-exchange](https://github.com/tommcdo/vim-exchange) feature does not have a default binding for visual mode, as the `shift-x` binding conflicts with the default `shift-x` binding for visual mode (`vim::VisualDeleteLine`). To assign the default vim-exchange binding, add the following keybinding to your keymap:
 
-```json [settings]
+```json [keymap]
 {
   "context": "vim_mode == visual",
   "bindings": {
@@ -587,7 +587,7 @@ Here's an example of these settings changed:
     "use_system_clipboard": "never",
     "use_smartcase_find": true,
     "gdefault": true,
-    "relative_line_numbers": "enabled",
+    "toggle_relative_line_numbers": true,
     "highlight_on_yank_duration": 50,
     "custom_digraphs": {
       "fz": "🧟‍♀️"
diff --git a/docs/src/visual-customization.md b/docs/src/visual-customization.md
index 43fb6d273e70d3b7d506db4235954d837fa62141..7f84fe8b289295c2562b5eba21ec512948da3552 100644
--- a/docs/src/visual-customization.md
+++ b/docs/src/visual-customization.md
@@ -483,13 +483,13 @@ Project panel can be shown/hidden with {#action project_panel::ToggleFocus} ({#k
 ## Agent Panel
 
 ```json [settings]
+{
   "agent": {
-    "version": "2",
-    "enabled": true,        // Enable/disable the agent
-    "button": true,         // Show/hide the icon in the status bar
-    "dock": "right",        // Where to dock: left, right, bottom
-    "default_width": 640,   // Default width (left/right docked)
-    "default_height": 320,  // Default height (bottom docked)
+    "enabled": true, // Enable/disable the agent
+    "button": true, // Show/hide the icon in the status bar
+    "dock": "right", // Where to dock: left, right, bottom
+    "default_width": 640, // Default width (left/right docked)
+    "default_height": 320 // Default height (bottom docked)
   },
   // Controls the font size for agent responses in the agent panel.
   // If not specified, it falls back to the UI font size.
@@ -497,6 +497,7 @@ Project panel can be shown/hidden with {#action project_panel::ToggleFocus} ({#k
   // Controls the font size for the agent panel's message editor, user message,
   // and any other snippet of code.
   "agent_buffer_font_size": 12
+}
 ```
 
 See [Zed AI Documentation](./ai/overview.md) for additional non-visual AI settings.