Autoclose brackets before a language-specific set of characters

Max Brunsfeld created

Fixes #588

Change summary

crates/editor/src/editor.rs           | 11 +++++------
crates/language/src/language.rs       |  8 ++++++++
crates/zed/languages/c/config.toml    |  1 +
crates/zed/languages/json/config.toml |  1 +
crates/zed/languages/rust/config.toml |  1 +
5 files changed, 16 insertions(+), 6 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1910,15 +1910,10 @@ impl Editor {
                             selection.start.saturating_sub(pair.start.len()),
                             &pair.start,
                         ) {
-                            // Autoclose only if the next character is a whitespace or a pair end
-                            // (possibly a different one from the pair we are inserting).
                             snapshot
                                 .chars_at(selection.start)
                                 .next()
-                                .map_or(true, |ch| ch.is_whitespace())
-                                || language.brackets().iter().any(|pair| {
-                                    snapshot.contains_str_at(selection.start, &pair.end)
-                                })
+                                .map_or(true, |c| language.should_autoclose_before(c))
                         } else {
                             false
                         }
@@ -8125,6 +8120,7 @@ mod tests {
                         newline: true,
                     },
                 ],
+                autoclose_before: "})]".to_string(),
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
@@ -8152,6 +8148,7 @@ mod tests {
                 ],
                 cx,
             );
+
             view.handle_input(&Input("{".to_string()), cx);
             view.handle_input(&Input("{".to_string()), cx);
             view.handle_input(&Input("{".to_string()), cx);
@@ -8215,6 +8212,8 @@ mod tests {
                 .unindent()
             );
 
+            // Don't autoclose if the next character isn't whitespace and isn't
+            // listed in the language's "autoclose_before" section.
             view.finalize_last_transaction(cx);
             view.select_display_ranges(&[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)], cx);
             view.handle_input(&Input("{".to_string()), cx);

crates/language/src/language.rs 🔗

@@ -49,6 +49,7 @@ lazy_static! {
             name: "Plain Text".into(),
             path_suffixes: Default::default(),
             brackets: Default::default(),
+            autoclose_before: Default::default(),
             line_comment: None,
             language_server: None,
         },
@@ -109,6 +110,8 @@ pub struct LanguageConfig {
     pub name: Arc<str>,
     pub path_suffixes: Vec<String>,
     pub brackets: Vec<BracketPair>,
+    #[serde(default)]
+    pub autoclose_before: String,
     pub line_comment: Option<String>,
     pub language_server: Option<LanguageServerConfig>,
 }
@@ -119,6 +122,7 @@ impl Default for LanguageConfig {
             name: "".into(),
             path_suffixes: Default::default(),
             brackets: Default::default(),
+            autoclose_before: Default::default(),
             line_comment: Default::default(),
             language_server: Default::default(),
         }
@@ -529,6 +533,10 @@ impl Language {
         &self.config.brackets
     }
 
+    pub fn should_autoclose_before(&self, c: char) -> bool {
+        c.is_whitespace() || self.config.autoclose_before.contains(c)
+    }
+
     pub fn set_theme(&self, theme: &SyntaxTheme) {
         if let Some(grammar) = self.grammar.as_ref() {
             *grammar.highlight_map.lock() =

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

@@ -1,6 +1,7 @@
 name = "C"
 path_suffixes = ["c", "h"]
 line_comment = "// "
+autoclose_before = ";:.,=}])>"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },

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

@@ -1,5 +1,6 @@
 name = "JSON"
 path_suffixes = ["json"]
+autoclose_before = ",]}"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },

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

@@ -1,6 +1,7 @@
 name = "Rust"
 path_suffixes = ["rs"]
 line_comment = "// "
+autoclose_before = ";:.,=}])>"
 brackets = [
     { start = "{", end = "}", close = true, newline = true },
     { start = "[", end = "]", close = true, newline = true },