Fix two auto-indent issues with Markdown and YAML (#20193)

Max Brunsfeld created

Closes #13376
Closes #13338

Release Notes:

- Fixed unhelpful auto-indent suggestions in markdown.
- Added `auto_indent_on_paste` setting, which can be used on a
per-language basis, to configure whether indentation should be adjusted
when pasting. This setting is enabled by default for languages other
than YAML and Markdown.

Change summary

assets/settings/default.json              |  2 ++
crates/editor/src/editor.rs               | 15 ++++++++++++---
crates/language/src/language.rs           |  4 ++++
crates/language/src/language_registry.rs  |  1 +
crates/language/src/language_settings.rs  |  7 +++++++
crates/languages/src/markdown/config.toml |  2 ++
crates/languages/src/yaml/config.toml     |  1 +
7 files changed, 29 insertions(+), 3 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -174,6 +174,8 @@
   // bracket, brace, single or double quote characters.
   // For example, when you select text and type (, Zed will surround the text with ().
   "use_auto_surround": true,
+  // Whether indentation of pasted content should be adjusted based on the context.
+  "auto_indent_on_paste": true,
   // Controls how the editor handles the autoclosed characters.
   // When set to `false`(default), skipping over and auto-removing of the closing characters
   // happen only for auto-inserted characters.

crates/editor/src/editor.rs 🔗

@@ -7280,9 +7280,14 @@ impl Editor {
                 if clipboard_selections.len() != old_selections.len() {
                     clipboard_selections.drain(..);
                 }
+                let cursor_offset = this.selections.last::<usize>(cx).head();
+                let mut auto_indent_on_paste = true;
 
                 this.buffer.update(cx, |buffer, cx| {
                     let snapshot = buffer.read(cx);
+                    auto_indent_on_paste =
+                        snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
+
                     let mut start_offset = 0;
                     let mut edits = Vec::new();
                     let mut original_indent_columns = Vec::new();
@@ -7321,9 +7326,13 @@ impl Editor {
 
                     buffer.edit(
                         edits,
-                        Some(AutoindentMode::Block {
-                            original_indent_columns,
-                        }),
+                        if auto_indent_on_paste {
+                            Some(AutoindentMode::Block {
+                                original_indent_columns,
+                            })
+                        } else {
+                            None
+                        },
                         cx,
                     );
                 });

crates/language/src/language.rs 🔗

@@ -591,6 +591,9 @@ pub struct LanguageConfig {
     /// the indentation level for a new line.
     #[serde(default = "auto_indent_using_last_non_empty_line_default")]
     pub auto_indent_using_last_non_empty_line: bool,
+    // Whether indentation of pasted content should be adjusted based on the context.
+    #[serde(default)]
+    pub auto_indent_on_paste: Option<bool>,
     /// A regex that is used to determine whether the indentation level should be
     /// increased in the following line.
     #[serde(default, deserialize_with = "deserialize_regex")]
@@ -718,6 +721,7 @@ impl Default for LanguageConfig {
             matcher: LanguageMatcher::default(),
             brackets: Default::default(),
             auto_indent_using_last_non_empty_line: auto_indent_using_last_non_empty_line_default(),
+            auto_indent_on_paste: None,
             increase_indent_pattern: Default::default(),
             decrease_indent_pattern: Default::default(),
             autoclose_before: Default::default(),

crates/language/src/language_registry.rs 🔗

@@ -965,6 +965,7 @@ impl LanguageRegistryState {
                 tab_size: language.config.tab_size,
                 hard_tabs: language.config.hard_tabs,
                 soft_wrap: language.config.soft_wrap,
+                auto_indent_on_paste: language.config.auto_indent_on_paste,
                 ..Default::default()
             }
             .clone(),

crates/language/src/language_settings.rs 🔗

@@ -125,6 +125,8 @@ pub struct LanguageSettings {
     /// Whether to use additional LSP queries to format (and amend) the code after
     /// every "trigger" symbol input, defined by LSP server capabilities.
     pub use_on_type_format: bool,
+    /// Whether indentation of pasted content should be adjusted based on the context.
+    pub auto_indent_on_paste: bool,
     // Controls how the editor handles the autoclosed characters.
     pub always_treat_brackets_as_autoclosed: bool,
     /// Which code actions to run on save
@@ -360,6 +362,10 @@ pub struct LanguageSettingsContent {
     ///
     /// Default: true
     pub linked_edits: Option<bool>,
+    /// Whether indentation of pasted content should be adjusted based on the context.
+    ///
+    /// Default: true
+    pub auto_indent_on_paste: Option<bool>,
     /// Task configuration for this language.
     ///
     /// Default: {}
@@ -1132,6 +1138,7 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
     merge(&mut settings.use_autoclose, src.use_autoclose);
     merge(&mut settings.use_auto_surround, src.use_auto_surround);
     merge(&mut settings.use_on_type_format, src.use_on_type_format);
+    merge(&mut settings.auto_indent_on_paste, src.auto_indent_on_paste);
     merge(
         &mut settings.always_treat_brackets_as_autoclosed,
         src.always_treat_brackets_as_autoclosed,

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

@@ -12,5 +12,7 @@ brackets = [
     { start = "`", end = "`", close = false, newline = false },
 ]
 
+auto_indent_on_paste = false
+auto_indent_using_last_non_empty_line = false
 tab_size = 2
 prettier_parser_name = "markdown"

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

@@ -10,6 +10,7 @@ brackets = [
     { start = "'", end = "'", close = true, newline = false, not_in = ["string"] },
 ]
 
+auto_indent_on_paste = false
 auto_indent_using_last_non_empty_line = false
 increase_indent_pattern = ":\\s*[|>]?\\s*$"
 prettier_parser_name = "yaml"