Merge pull request #2193 from zed-industries/autoclose-fixes

Max Brunsfeld created

Restructure scope-specific auto-close pairs, fix regression in skipping over auto-closed brackets

Change summary

crates/editor/src/editor.rs                     |   9 
crates/editor/src/editor_tests.rs               | 359 ++++++++++++------
crates/editor/src/highlight_matching_bracket.rs |  36 +
crates/language/src/buffer_tests.rs             |  80 ++--
crates/language/src/language.rs                 | 126 +++++-
crates/zed/src/languages/c/config.toml          |  20 
crates/zed/src/languages/cpp/config.toml        |  20 
crates/zed/src/languages/css/config.toml        |  18 
crates/zed/src/languages/elixir/config.toml     |  18 
crates/zed/src/languages/go/config.toml         |  20 
crates/zed/src/languages/html/config.toml       |  20 
crates/zed/src/languages/javascript/config.toml |  24 -
crates/zed/src/languages/json/config.toml       |   8 
crates/zed/src/languages/lua/config.toml        |  12 
crates/zed/src/languages/python/config.toml     |  24 -
crates/zed/src/languages/ruby/config.toml       |  18 
crates/zed/src/languages/rust/config.toml       |  23 -
crates/zed/src/languages/scheme/config.toml     |  14 
crates/zed/src/languages/toml/config.toml       |  16 
crates/zed/src/languages/tsx/config.toml        |  15 
crates/zed/src/languages/tsx/overrides.scm      |   3 
crates/zed/src/languages/typescript/config.toml |  24 -
crates/zed/src/languages/yaml/config.toml       |   8 
crates/zed/src/zed.rs                           |  15 
24 files changed, 474 insertions(+), 456 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1761,8 +1761,8 @@ impl Editor {
                 // bracket of any of this language's bracket pairs.
                 let mut bracket_pair = None;
                 let mut is_bracket_pair_start = false;
-                for pair in language.brackets() {
-                    if pair.close && pair.start.ends_with(text.as_ref()) {
+                for (pair, enabled) in language.brackets() {
+                    if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
                         bracket_pair = Some(pair.clone());
                         is_bracket_pair_start = true;
                         break;
@@ -1930,11 +1930,12 @@ impl Editor {
                                 .map(|c| c.len_utf8())
                                 .sum::<usize>();
 
-                            insert_extra_newline = language.brackets().iter().any(|pair| {
+                            insert_extra_newline = language.brackets().any(|(pair, enabled)| {
                                 let pair_start = pair.start.trim_end();
                                 let pair_end = pair.end.trim_start();
 
-                                pair.newline
+                                enabled
+                                    && pair.newline
                                     && buffer
                                         .contains_str_at(end + trailing_whitespace_len, pair_end)
                                     && buffer.contains_str_at(

crates/editor/src/editor_tests.rs 🔗

@@ -13,8 +13,9 @@ use gpui::{
     executor::Deterministic,
     geometry::{rect::RectF, vector::vec2f},
     platform::{WindowBounds, WindowOptions},
+    serde_json,
 };
-use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
+use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
 use project::FakeFs;
 use settings::EditorSettings;
 use util::{
@@ -3002,20 +3003,23 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
     let language = Arc::new(
         Language::new(
             LanguageConfig {
-                brackets: vec![
-                    BracketPair {
-                        start: "{".to_string(),
-                        end: "}".to_string(),
-                        close: false,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "(".to_string(),
-                        end: ")".to_string(),
-                        close: false,
-                        newline: true,
-                    },
-                ],
+                brackets: BracketPairConfig {
+                    pairs: vec![
+                        BracketPair {
+                            start: "{".to_string(),
+                            end: "}".to_string(),
+                            close: false,
+                            newline: true,
+                        },
+                        BracketPair {
+                            start: "(".to_string(),
+                            end: ")".to_string(),
+                            close: false,
+                            newline: true,
+                        },
+                    ],
+                    ..Default::default()
+                },
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
@@ -3059,38 +3063,41 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
 
     let language = Arc::new(Language::new(
         LanguageConfig {
-            brackets: vec![
-                BracketPair {
-                    start: "{".to_string(),
-                    end: "}".to_string(),
-                    close: true,
-                    newline: true,
-                },
-                BracketPair {
-                    start: "(".to_string(),
-                    end: ")".to_string(),
-                    close: true,
-                    newline: true,
-                },
-                BracketPair {
-                    start: "/*".to_string(),
-                    end: " */".to_string(),
-                    close: true,
-                    newline: true,
-                },
-                BracketPair {
-                    start: "[".to_string(),
-                    end: "]".to_string(),
-                    close: false,
-                    newline: true,
-                },
-                BracketPair {
-                    start: "\"".to_string(),
-                    end: "\"".to_string(),
-                    close: true,
-                    newline: false,
-                },
-            ],
+            brackets: BracketPairConfig {
+                pairs: vec![
+                    BracketPair {
+                        start: "{".to_string(),
+                        end: "}".to_string(),
+                        close: true,
+                        newline: true,
+                    },
+                    BracketPair {
+                        start: "(".to_string(),
+                        end: ")".to_string(),
+                        close: true,
+                        newline: true,
+                    },
+                    BracketPair {
+                        start: "/*".to_string(),
+                        end: " */".to_string(),
+                        close: true,
+                        newline: true,
+                    },
+                    BracketPair {
+                        start: "[".to_string(),
+                        end: "]".to_string(),
+                        close: false,
+                        newline: true,
+                    },
+                    BracketPair {
+                        start: "\"".to_string(),
+                        end: "\"".to_string(),
+                        close: true,
+                        newline: false,
+                    },
+                ],
+                ..Default::default()
+            },
             autoclose_before: "})]".to_string(),
             ..Default::default()
         },
@@ -3227,10 +3234,52 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
         Language::new(
             LanguageConfig {
                 name: "HTML".into(),
-                brackets: vec![
+                brackets: BracketPairConfig {
+                    pairs: vec![
+                        BracketPair {
+                            start: "<".into(),
+                            end: ">".into(),
+                            close: true,
+                            ..Default::default()
+                        },
+                        BracketPair {
+                            start: "{".into(),
+                            end: "}".into(),
+                            close: true,
+                            ..Default::default()
+                        },
+                        BracketPair {
+                            start: "(".into(),
+                            end: ")".into(),
+                            close: true,
+                            ..Default::default()
+                        },
+                    ],
+                    ..Default::default()
+                },
+                autoclose_before: "})]>".into(),
+                ..Default::default()
+            },
+            Some(tree_sitter_html::language()),
+        )
+        .with_injection_query(
+            r#"
+            (script_element
+                (raw_text) @content
+                (#set! "language" "javascript"))
+            "#,
+        )
+        .unwrap(),
+    );
+
+    let javascript_language = Arc::new(Language::new(
+        LanguageConfig {
+            name: "JavaScript".into(),
+            brackets: BracketPairConfig {
+                pairs: vec![
                     BracketPair {
-                        start: "<".into(),
-                        end: ">".into(),
+                        start: "/*".into(),
+                        end: " */".into(),
                         close: true,
                         ..Default::default()
                     },
@@ -3247,44 +3296,8 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
                         ..Default::default()
                     },
                 ],
-                autoclose_before: "})]>".into(),
                 ..Default::default()
             },
-            Some(tree_sitter_html::language()),
-        )
-        .with_injection_query(
-            r#"
-            (script_element
-                (raw_text) @content
-                (#set! "language" "javascript"))
-            "#,
-        )
-        .unwrap(),
-    );
-
-    let javascript_language = Arc::new(Language::new(
-        LanguageConfig {
-            name: "JavaScript".into(),
-            brackets: vec![
-                BracketPair {
-                    start: "/*".into(),
-                    end: " */".into(),
-                    close: true,
-                    ..Default::default()
-                },
-                BracketPair {
-                    start: "{".into(),
-                    end: "}".into(),
-                    close: true,
-                    ..Default::default()
-                },
-                BracketPair {
-                    start: "(".into(),
-                    end: ")".into(),
-                    close: true,
-                    ..Default::default()
-                },
-            ],
             autoclose_before: "})]>".into(),
             ..Default::default()
         },
@@ -3447,25 +3460,125 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
     );
 }
 
+#[gpui::test]
+async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
+    let mut cx = EditorTestContext::new(cx);
+
+    let rust_language = Arc::new(
+        Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                brackets: serde_json::from_value(json!([
+                    { "start": "{", "end": "}", "close": true, "newline": true },
+                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
+                ]))
+                .unwrap(),
+                autoclose_before: "})]>".into(),
+                ..Default::default()
+            },
+            Some(tree_sitter_rust::language()),
+        )
+        .with_override_query("(string_literal) @string")
+        .unwrap(),
+    );
+
+    let registry = Arc::new(LanguageRegistry::test());
+    registry.add(rust_language.clone());
+
+    cx.update_buffer(|buffer, cx| {
+        buffer.set_language_registry(registry);
+        buffer.set_language(Some(rust_language), cx);
+    });
+
+    cx.set_state(
+        &r#"
+            let x = ˇ
+        "#
+        .unindent(),
+    );
+
+    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
+    cx.update_editor(|editor, cx| {
+        editor.handle_input("\"", cx);
+    });
+    cx.assert_editor_state(
+        &r#"
+            let x = "ˇ"
+        "#
+        .unindent(),
+    );
+
+    // Inserting another quotation mark. The cursor moves across the existing
+    // automatically-inserted quotation mark.
+    cx.update_editor(|editor, cx| {
+        editor.handle_input("\"", cx);
+    });
+    cx.assert_editor_state(
+        &r#"
+            let x = ""ˇ
+        "#
+        .unindent(),
+    );
+
+    // Reset
+    cx.set_state(
+        &r#"
+            let x = ˇ
+        "#
+        .unindent(),
+    );
+
+    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
+    cx.update_editor(|editor, cx| {
+        editor.handle_input("\"", cx);
+        editor.handle_input(" ", cx);
+        editor.move_left(&Default::default(), cx);
+        editor.handle_input("\\", cx);
+        editor.handle_input("\"", cx);
+    });
+    cx.assert_editor_state(
+        &r#"
+            let x = "\"ˇ "
+        "#
+        .unindent(),
+    );
+
+    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
+    // mark. Nothing is inserted.
+    cx.update_editor(|editor, cx| {
+        editor.move_right(&Default::default(), cx);
+        editor.handle_input("\"", cx);
+    });
+    cx.assert_editor_state(
+        &r#"
+            let x = "\" "ˇ
+        "#
+        .unindent(),
+    );
+}
+
 #[gpui::test]
 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
     cx.update(|cx| cx.set_global(Settings::test(cx)));
     let language = Arc::new(Language::new(
         LanguageConfig {
-            brackets: vec![
-                BracketPair {
-                    start: "{".to_string(),
-                    end: "}".to_string(),
-                    close: true,
-                    newline: true,
-                },
-                BracketPair {
-                    start: "/* ".to_string(),
-                    end: "*/".to_string(),
-                    close: true,
-                    ..Default::default()
-                },
-            ],
+            brackets: BracketPairConfig {
+                pairs: vec![
+                    BracketPair {
+                        start: "{".to_string(),
+                        end: "}".to_string(),
+                        close: true,
+                        newline: true,
+                    },
+                    BracketPair {
+                        start: "/* ".to_string(),
+                        end: "*/".to_string(),
+                        close: true,
+                        ..Default::default()
+                    },
+                ],
+                ..Default::default()
+            },
             ..Default::default()
         },
         Some(tree_sitter_rust::language()),
@@ -3603,12 +3716,15 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
     cx.update(|cx| cx.set_global(Settings::test(cx)));
     let language = Arc::new(Language::new(
         LanguageConfig {
-            brackets: vec![BracketPair {
-                start: "{".to_string(),
-                end: "}".to_string(),
-                close: true,
-                newline: true,
-            }],
+            brackets: BracketPairConfig {
+                pairs: vec![BracketPair {
+                    start: "{".to_string(),
+                    end: "}".to_string(),
+                    close: true,
+                    newline: true,
+                }],
+                ..Default::default()
+            },
             autoclose_before: "}".to_string(),
             ..Default::default()
         },
@@ -5030,20 +5146,23 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
     let language = Arc::new(
         Language::new(
             LanguageConfig {
-                brackets: vec![
-                    BracketPair {
-                        start: "{".to_string(),
-                        end: "}".to_string(),
-                        close: true,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "/* ".to_string(),
-                        end: " */".to_string(),
-                        close: true,
-                        newline: true,
-                    },
-                ],
+                brackets: BracketPairConfig {
+                    pairs: vec![
+                        BracketPair {
+                            start: "{".to_string(),
+                            end: "}".to_string(),
+                            close: true,
+                            newline: true,
+                        },
+                        BracketPair {
+                            start: "/* ".to_string(),
+                            end: " */".to_string(),
+                            close: true,
+                            newline: true,
+                        },
+                    ],
+                    ..Default::default()
+                },
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),

crates/editor/src/highlight_matching_bracket.rs 🔗

@@ -32,11 +32,10 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
 
 #[cfg(test)]
 mod tests {
-    use crate::test::editor_lsp_test_context::EditorLspTestContext;
-
     use super::*;
+    use crate::test::editor_lsp_test_context::EditorLspTestContext;
     use indoc::indoc;
-    use language::{BracketPair, Language, LanguageConfig};
+    use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
 
     #[gpui::test]
     async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
@@ -45,20 +44,23 @@ mod tests {
                 LanguageConfig {
                     name: "Rust".into(),
                     path_suffixes: vec!["rs".to_string()],
-                    brackets: vec![
-                        BracketPair {
-                            start: "{".to_string(),
-                            end: "}".to_string(),
-                            close: false,
-                            newline: true,
-                        },
-                        BracketPair {
-                            start: "(".to_string(),
-                            end: ")".to_string(),
-                            close: false,
-                            newline: true,
-                        },
-                    ],
+                    brackets: BracketPairConfig {
+                        pairs: vec![
+                            BracketPair {
+                                start: "{".to_string(),
+                                end: "}".to_string(),
+                                close: false,
+                                newline: true,
+                            },
+                            BracketPair {
+                                start: "(".to_string(),
+                                end: ")".to_string(),
+                                close: false,
+                                newline: true,
+                            },
+                        ],
+                        ..Default::default()
+                    },
                     ..Default::default()
                 },
                 Some(tree_sitter_rust::language()),

crates/language/src/buffer_tests.rs 🔗

@@ -1527,42 +1527,34 @@ fn test_language_config_at(cx: &mut MutableAppContext) {
             LanguageConfig {
                 name: "JavaScript".into(),
                 line_comment: Some("// ".into()),
-                brackets: vec![
-                    BracketPair {
-                        start: "{".into(),
-                        end: "}".into(),
-                        close: true,
-                        newline: false,
-                    },
-                    BracketPair {
-                        start: "'".into(),
-                        end: "'".into(),
-                        close: true,
-                        newline: false,
-                    },
-                ],
-                overrides: [
-                    (
-                        "element".into(),
-                        LanguageConfigOverride {
-                            line_comment: Override::Remove { remove: true },
-                            block_comment: Override::Set(("{/*".into(), "*/}".into())),
-                            ..Default::default()
+                brackets: BracketPairConfig {
+                    pairs: vec![
+                        BracketPair {
+                            start: "{".into(),
+                            end: "}".into(),
+                            close: true,
+                            newline: false,
                         },
-                    ),
-                    (
-                        "string".into(),
-                        LanguageConfigOverride {
-                            brackets: Override::Set(vec![BracketPair {
-                                start: "{".into(),
-                                end: "}".into(),
-                                close: true,
-                                newline: false,
-                            }]),
-                            ..Default::default()
+                        BracketPair {
+                            start: "'".into(),
+                            end: "'".into(),
+                            close: true,
+                            newline: false,
                         },
-                    ),
-                ]
+                    ],
+                    disabled_scopes_by_bracket_ix: vec![
+                        Vec::new(), //
+                        vec!["string".into()],
+                    ],
+                },
+                overrides: [(
+                    "element".into(),
+                    LanguageConfigOverride {
+                        line_comment: Override::Remove { remove: true },
+                        block_comment: Override::Set(("{/*".into(), "*/}".into())),
+                        ..Default::default()
+                    },
+                )]
                 .into_iter()
                 .collect(),
                 ..Default::default()
@@ -1584,11 +1576,19 @@ fn test_language_config_at(cx: &mut MutableAppContext) {
 
         let config = snapshot.language_scope_at(0).unwrap();
         assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
-        assert_eq!(config.brackets().len(), 2);
+        // Both bracket pairs are enabled
+        assert_eq!(
+            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+            &[true, true]
+        );
 
         let string_config = snapshot.language_scope_at(3).unwrap();
-        assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
-        assert_eq!(string_config.brackets().len(), 1);
+        assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
+        // Second bracket pair is disabled
+        assert_eq!(
+            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+            &[true, false]
+        );
 
         let element_config = snapshot.language_scope_at(10).unwrap();
         assert_eq!(element_config.line_comment_prefix(), None);
@@ -1596,7 +1596,11 @@ fn test_language_config_at(cx: &mut MutableAppContext) {
             element_config.block_comment_delimiters(),
             Some((&"{/*".into(), &"*/}".into()))
         );
-        assert_eq!(element_config.brackets().len(), 2);
+        // Both bracket pairs are enabled
+        assert_eq!(
+            element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
+            &[true, true]
+        );
 
         buffer
     });

crates/language/src/language.rs 🔗

@@ -231,7 +231,7 @@ pub struct CodeLabel {
 pub struct LanguageConfig {
     pub name: Arc<str>,
     pub path_suffixes: Vec<String>,
-    pub brackets: Vec<BracketPair>,
+    pub brackets: BracketPairConfig,
     #[serde(default = "auto_indent_using_last_non_empty_line_default")]
     pub auto_indent_using_last_non_empty_line: bool,
     #[serde(default, deserialize_with = "deserialize_regex")]
@@ -258,7 +258,7 @@ pub struct LanguageQueries {
     pub overrides: Option<Cow<'static, str>>,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct LanguageScope {
     language: Arc<Language>,
     override_id: Option<u32>,
@@ -270,8 +270,8 @@ pub struct LanguageConfigOverride {
     pub line_comment: Override<Arc<str>>,
     #[serde(default)]
     pub block_comment: Override<(Arc<str>, Arc<str>)>,
-    #[serde(default)]
-    pub brackets: Override<Vec<BracketPair>>,
+    #[serde(skip_deserializing)]
+    pub disabled_bracket_ixs: Vec<u16>,
 }
 
 #[derive(Deserialize, Debug)]
@@ -336,7 +336,41 @@ pub struct FakeLspAdapter {
     pub disk_based_diagnostics_sources: Vec<String>,
 }
 
-#[derive(Clone, Debug, Default, Deserialize)]
+#[derive(Clone, Debug, Default)]
+pub struct BracketPairConfig {
+    pub pairs: Vec<BracketPair>,
+    pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
+}
+
+impl<'de> Deserialize<'de> for BracketPairConfig {
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize)]
+        pub struct Entry {
+            #[serde(flatten)]
+            pub bracket_pair: BracketPair,
+            #[serde(default)]
+            pub not_in: Vec<String>,
+        }
+
+        let result = Vec::<Entry>::deserialize(deserializer)?;
+        let mut brackets = Vec::with_capacity(result.len());
+        let mut disabled_scopes_by_bracket_ix = Vec::with_capacity(result.len());
+        for entry in result {
+            brackets.push(entry.bracket_pair);
+            disabled_scopes_by_bracket_ix.push(entry.not_in);
+        }
+
+        Ok(BracketPairConfig {
+            pairs: brackets,
+            disabled_scopes_by_bracket_ix,
+        })
+    }
+}
+
+#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
 pub struct BracketPair {
     pub start: String,
     pub end: String,
@@ -393,7 +427,7 @@ struct InjectionConfig {
 
 struct OverrideConfig {
     query: Query,
-    values: HashMap<u32, LanguageConfigOverride>,
+    values: HashMap<u32, (String, LanguageConfigOverride)>,
 }
 
 #[derive(Default, Clone)]
@@ -967,16 +1001,11 @@ impl Language {
     pub fn with_override_query(mut self, source: &str) -> Result<Self> {
         let query = Query::new(self.grammar_mut().ts_language, source)?;
 
-        let mut values = HashMap::default();
+        let mut override_configs_by_id = HashMap::default();
         for (ix, name) in query.capture_names().iter().enumerate() {
             if !name.starts_with('_') {
-                let value = self.config.overrides.remove(name).ok_or_else(|| {
-                    anyhow!(
-                        "language {:?} has override in query but not in config: {name:?}",
-                        self.config.name
-                    )
-                })?;
-                values.insert(ix as u32, value);
+                let value = self.config.overrides.remove(name).unwrap_or_default();
+                override_configs_by_id.insert(ix as u32, (name.clone(), value));
             }
         }
 
@@ -988,7 +1017,46 @@ impl Language {
             ))?;
         }
 
-        self.grammar_mut().override_config = Some(OverrideConfig { query, values });
+        for disabled_scope_name in self
+            .config
+            .brackets
+            .disabled_scopes_by_bracket_ix
+            .iter()
+            .flatten()
+        {
+            if !override_configs_by_id
+                .values()
+                .any(|(scope_name, _)| scope_name == disabled_scope_name)
+            {
+                Err(anyhow!(
+                    "language {:?} has overrides in config not in query: {disabled_scope_name:?}",
+                    self.config.name
+                ))?;
+            }
+        }
+
+        for (name, override_config) in override_configs_by_id.values_mut() {
+            override_config.disabled_bracket_ixs = self
+                .config
+                .brackets
+                .disabled_scopes_by_bracket_ix
+                .iter()
+                .enumerate()
+                .filter_map(|(ix, disabled_scope_names)| {
+                    if disabled_scope_names.contains(name) {
+                        Some(ix as u16)
+                    } else {
+                        None
+                    }
+                })
+                .collect();
+        }
+
+        self.config.brackets.disabled_scopes_by_bracket_ix.clear();
+        self.grammar_mut().override_config = Some(OverrideConfig {
+            query,
+            values: override_configs_by_id,
+        });
         Ok(self)
     }
 
@@ -1132,12 +1200,26 @@ impl LanguageScope {
         .map(|e| (&e.0, &e.1))
     }
 
-    pub fn brackets(&self) -> &[BracketPair] {
-        Override::as_option(
-            self.config_override().map(|o| &o.brackets),
-            Some(&self.language.config.brackets),
-        )
-        .map_or(&[], Vec::as_slice)
+    pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
+        let mut disabled_ids = self
+            .config_override()
+            .map_or(&[] as _, |o| o.disabled_bracket_ixs.as_slice());
+        self.language
+            .config
+            .brackets
+            .pairs
+            .iter()
+            .enumerate()
+            .map(move |(ix, bracket)| {
+                let mut is_enabled = true;
+                if let Some(next_disabled_ix) = disabled_ids.first() {
+                    if ix == *next_disabled_ix as usize {
+                        disabled_ids = &disabled_ids[1..];
+                        is_enabled = false;
+                    }
+                }
+                (bracket, is_enabled)
+            })
     }
 
     pub fn should_autoclose_before(&self, c: char) -> bool {
@@ -1148,7 +1230,7 @@ impl LanguageScope {
         let id = self.override_id?;
         let grammar = self.language.grammar.as_ref()?;
         let override_config = grammar.override_config.as_ref()?;
-        override_config.values.get(&id)
+        override_config.values.get(&id).map(|e| &e.1)
     }
 }
 

crates/zed/src/languages/c/config.toml 🔗

@@ -6,21 +6,7 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]

crates/zed/src/languages/cpp/config.toml 🔗

@@ -6,21 +6,7 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]

crates/zed/src/languages/css/config.toml 🔗

@@ -5,20 +5,6 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
 ]

crates/zed/src/languages/elixir/config.toml 🔗

@@ -6,20 +6,6 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
 ]

crates/zed/src/languages/go/config.toml 🔗

@@ -6,21 +6,7 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
 ]

crates/zed/src/languages/html/config.toml 🔗

@@ -1,26 +1,12 @@
 name = "HTML"
 path_suffixes = ["html"]
 autoclose_before = ">})"
-brackets = [
-    { start = "<", end = ">", close = true, newline = true },
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "!--", end = " --", close = true, newline = false },
-]
-
 block_comment = ["<!-- ", " -->"]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "<", end = ">", close = true, newline = true, not_in = ["comment", "string"] },
+    { start = "!--", end = " --", close = true, newline = false, not_in = ["comment", "string"] },
 ]

crates/zed/src/languages/javascript/config.toml 🔗

@@ -6,25 +6,11 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = false, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-    { start = "`", end = "`", close = true, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "<", end = ">", close = false, newline = true, not_in = ["comment", "string"] },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "`", end = "`", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
 ]
 
 [overrides.element]

crates/zed/src/languages/json/config.toml 🔗

@@ -5,11 +5,5 @@ autoclose_before = ",]}"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
 ]

crates/zed/src/languages/lua/config.toml 🔗

@@ -3,13 +3,7 @@ path_suffixes = ["lua"]
 line_comment = "-- "
 autoclose_before = ",]}"
 brackets = [
-{ start = "{", end = "}", close = true, newline = true },
-{ start = "[", end = "]", close = true, newline = true },
-{ start = "\"", end = "\"", close = true, newline = false },
+    { start = "{", end = "}", close = true, newline = true },
+    { start = "[", end = "]", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
 ]
-
-[overrides.string]
-brackets = [
-{ start = "{", end = "}", close = true, newline = true },
-{ start = "[", end = "]", close = true, newline = true },
-]

crates/zed/src/languages/python/config.toml 🔗

@@ -2,28 +2,14 @@ name = "Python"
 path_suffixes = ["py", "pyi"]
 line_comment = "# "
 autoclose_before = ";:.,=}])>"
-brackets = [
-  { start = "{", end = "}", close = true, newline = true },
-  { start = "[", end = "]", close = true, newline = true },
-  { start = "(", end = ")", close = true, newline = true },
-  { start = "\"", end = "\"", close = true, newline = false },
-  { start = "'", end = "'", close = false, newline = false },
-]
-
-auto_indent_using_last_non_empty_line = false
-increase_indent_pattern = ":\\s*$"
-decrease_indent_pattern = "^\\s*(else|elif|except|finally)\\b.*:"
-
-[overrides.comment]
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+    { start = "'", end = "'", close = false, newline = false, not_in = ["string"] },
 ]
 
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
+auto_indent_using_last_non_empty_line = false
+increase_indent_pattern = ":\\s*$"
+decrease_indent_pattern = "^\\s*(else|elif|except|finally)\\b.*:"

crates/zed/src/languages/ruby/config.toml 🔗

@@ -6,20 +6,6 @@ brackets = [
   { start = "{", end = "}", close = true, newline = true },
   { start = "[", end = "]", close = true, newline = true },
   { start = "(", end = ")", close = true, newline = true },
-  { start = "\"", end = "\"", close = true, newline = false },
-  { start = "'", end = "'", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+  { start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
+  { start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
 ]

crates/zed/src/languages/rust/config.toml 🔗

@@ -6,24 +6,7 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = false, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = false, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = false, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = false, newline = true },
+    { start = "<", end = ">", close = false, newline = true, not_in = ["string", "comment"] },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]

crates/zed/src/languages/scheme/config.toml 🔗

@@ -5,17 +5,5 @@ autoclose_before = "])"
 brackets = [
     { start = "[", end = "]", close = true, newline = false },
     { start = "(", end = ")", close = true, newline = false },
-    { start = "\"", end = "\"", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
 ]

crates/zed/src/languages/toml/config.toml 🔗

@@ -5,18 +5,6 @@ autoclose_before = ",]}"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
 ]

crates/zed/src/languages/tsx/config.toml 🔗

@@ -6,18 +6,13 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = false, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-    { start = "`", end = "`", close = true, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
+    { start = "<", end = ">", close = false, newline = true, not_in = ["string", "comment"] },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "`", end = "`", close = true, newline = false, not_in = ["string"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]
 
 [overrides.element]
 line_comment = { remove = true }
 block_comment = ["{/* ", " */}"]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-]

crates/zed/src/languages/typescript/config.toml 🔗

@@ -6,23 +6,9 @@ brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
     { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = false, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
-    { start = "'", end = "'", close = true, newline = false },
-    { start = "`", end = "`", close = true, newline = false },
-    { start = "/*", end = " */", close = true, newline = false },
-]
-
-[overrides.comment]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-]
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
+    { start = "<", end = ">", close = false, newline = true, not_in = ["string", "comment"] },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
+    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "`", end = "`", close = true, newline = false, not_in = ["string"] },
+    { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
 ]

crates/zed/src/languages/yaml/config.toml 🔗

@@ -5,13 +5,7 @@ autoclose_before = ",]}"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },
-    { start = "\"", end = "\"", close = true, newline = false },
+    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
 ]
 
 increase_indent_pattern = ":\\s*[|>]?\\s*$"
-
-[overrides.string]
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-]

crates/zed/src/zed.rs 🔗

@@ -701,8 +701,9 @@ mod tests {
     use assets::Assets;
     use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
     use gpui::{
-        executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle,
+        executor::Deterministic, AssetSource, MutableAppContext, Task, TestAppContext, ViewHandle,
     };
+    use language::LanguageRegistry;
     use project::{Project, ProjectPath};
     use serde_json::json;
     use std::{
@@ -1886,6 +1887,18 @@ mod tests {
         assert!(has_default_theme);
     }
 
+    #[gpui::test]
+    fn test_bundled_languages(cx: &mut MutableAppContext) {
+        let mut languages = LanguageRegistry::new(Task::ready(()));
+        languages.set_executor(cx.background().clone());
+        let languages = Arc::new(languages);
+        languages::init(languages.clone());
+        for name in languages.language_names() {
+            languages.language_for_name(&name);
+        }
+        cx.foreground().run_until_parked();
+    }
+
     fn init(cx: &mut TestAppContext) -> Arc<AppState> {
         cx.foreground().forbid_parking();
         cx.update(|cx| {