language: Add auto-surround for Plain Text, JSON, and JSONC (#42631)

Mustaque Ahmed and Smit Barmase created

**Summary**
When users selected text and pressed opening brackets (`(`, `[`, `{`),
the text was deleted instead of being wrapped.

- Added bracket pairs: `()`, `[]`, `{}`, `""`, `''` with `surround =
true`
- Added `surround = true` to existing bracket pairs
- Added `()` bracket pair

**Production Build Fix** (`crates/languages/src/lib.rs`)
- Fixed bug where `brackets` config was stripped in non-`load-grammars`
builds
- Preserved `brackets: config.brackets` in production mode

Closes #41186

**Screen recording**

https://github.com/user-attachments/assets/22067fe7-d5c4-4a72-a93d-8dbaae640168

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>

Change summary

crates/language/src/language.rs        | 40 ++++++++++++++++++++++++++++
crates/languages/src/json/config.toml  |  7 ++--
crates/languages/src/jsonc/config.toml |  7 ++--
3 files changed, 48 insertions(+), 6 deletions(-)

Detailed changes

crates/language/src/language.rs 🔗

@@ -136,6 +136,46 @@ pub static PLAIN_TEXT: LazyLock<Arc<Language>> = LazyLock::new(|| {
                 path_suffixes: vec!["txt".to_owned()],
                 first_line_pattern: None,
             },
+            brackets: BracketPairConfig {
+                pairs: vec![
+                    BracketPair {
+                        start: "(".to_string(),
+                        end: ")".to_string(),
+                        close: true,
+                        surround: true,
+                        newline: false,
+                    },
+                    BracketPair {
+                        start: "[".to_string(),
+                        end: "]".to_string(),
+                        close: true,
+                        surround: true,
+                        newline: false,
+                    },
+                    BracketPair {
+                        start: "{".to_string(),
+                        end: "}".to_string(),
+                        close: true,
+                        surround: true,
+                        newline: false,
+                    },
+                    BracketPair {
+                        start: "\"".to_string(),
+                        end: "\"".to_string(),
+                        close: true,
+                        surround: true,
+                        newline: false,
+                    },
+                    BracketPair {
+                        start: "'".to_string(),
+                        end: "'".to_string(),
+                        close: true,
+                        surround: true,
+                        newline: false,
+                    },
+                ],
+                disabled_scopes_by_bracket_ix: Default::default(),
+            },
             ..Default::default()
         },
         None,

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

@@ -4,9 +4,10 @@ path_suffixes = ["json", "flake.lock"]
 line_comments = ["// "]
 autoclose_before = ",]}"
 brackets = [
-    { 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, surround = true, newline = true },
+    { start = "[", end = "]", close = true, surround = true, newline = true },
+    { start = "(", end = ")", close = true, surround = true, newline = false },
+    { start = "\"", end = "\"", close = true, surround = true, newline = false, not_in = ["string"] },
 ]
 tab_size = 2
 prettier_parser_name = "json"

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

@@ -4,9 +4,10 @@ path_suffixes = ["jsonc", "bun.lock", "tsconfig.json", "pyrightconfig.json"]
 line_comments = ["// "]
 autoclose_before = ",]}"
 brackets = [
-    { 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, surround = true, newline = true },
+    { start = "[", end = "]", close = true, surround = true, newline = true },
+    { start = "(", end = ")", close = true, surround = true, newline = false },
+    { start = "\"", end = "\"", close = true, surround = true, newline = false, not_in = ["string"] },
 ]
 tab_size = 2
 prettier_parser_name = "jsonc"