python: Auto-close f-strings (#28709)

Ben Kunkle created

Closes #28707

Release Notes:

- Added support for auto-closing `f`, `b`, `u`, `r`, `rb` and the newly
released `t` strings in Python

Change summary

crates/editor/src/editor.rs             | 14 ++++++++++----
crates/languages/src/python/config.toml | 12 ++++++++++++
2 files changed, 22 insertions(+), 4 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -3169,6 +3169,7 @@ impl Editor {
                 let mut is_bracket_pair_start = false;
                 let mut is_bracket_pair_end = false;
                 if !text.is_empty() {
+                    let mut bracket_pair_matching_end = None;
                     // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
                     //  and they are removing the character that triggered IME popup.
                     for (pair, enabled) in scope.brackets() {
@@ -3193,12 +3194,17 @@ impl Editor {
                                 break;
                             }
                         }
-                        if pair.end.as_str() == text.as_ref() {
-                            bracket_pair = Some(pair.clone());
-                            is_bracket_pair_end = true;
-                            break;
+                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
+                        {
+                            // take first bracket pair matching end, but don't break in case a later bracket
+                            // pair matches start
+                            bracket_pair_matching_end = Some(pair.clone());
                         }
                     }
+                    if bracket_pair.is_none() && bracket_pair_matching_end.is_some() {
+                        bracket_pair = Some(bracket_pair_matching_end.unwrap());
+                        is_bracket_pair_end = true;
+                    }
                 }
 
                 if let Some(bracket_pair) = bracket_pair {

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

@@ -5,6 +5,18 @@ first_line_pattern = '^#!.*\bpython[0-9.]*\b'
 line_comments = ["# "]
 autoclose_before = ";:.,=}])>"
 brackets = [
+    { start = "f\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "f'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "b\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "b'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "u\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "u'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "r\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "r'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "rb\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "rb'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "t\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
+    { start = "t'", 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"] },
     { start = "{", end = "}", close = true, newline = true },