markdown: Don't adjust indentation when inserting with multiple cursors (#40794)

vipex and Smit Barmase created

Closes #40757

## Summary

This PR addresses an issue where Zed incorrectly adjusts the indentation
of Markdown lists when inserting text using multiple cursors. Currently:

- Editing individual lines with a single cursor behaves correctly (no
unwanted indentation changes).
- Using multiple cursors, Zed automatically adjusts the indentation,
unlike VS Code, which preserves the existing formatting.

## Tasks
- [x] Implement a new test to verify correct Markdown indentation
behavior with multiple cursors.
- [x] Apply the fix to prevent Zed from adjusting indentation when
inserting text on multiple cursors.

------------------------

Release Notes:

- Fixed an issue where inserting text with multiple cursors inside a
nested Markdown list would cause it to lose its indentation.

---------

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

Change summary

Cargo.lock                                |  1 
crates/editor/Cargo.toml                  |  1 
crates/editor/src/editor_tests.rs         | 59 +++++++++++++++++++++++++
crates/languages/src/markdown/config.toml |  1 
crates/languages/src/markdown/indents.scm |  3 +
5 files changed, 65 insertions(+)

Detailed changes

Cargo.lock πŸ”—

@@ -5405,6 +5405,7 @@ dependencies = [
  "tree-sitter-bash",
  "tree-sitter-c",
  "tree-sitter-html",
+ "tree-sitter-md",
  "tree-sitter-python",
  "tree-sitter-rust",
  "tree-sitter-typescript",

crates/editor/Cargo.toml πŸ”—

@@ -118,6 +118,7 @@ tree-sitter-rust.workspace = true
 tree-sitter-typescript.workspace = true
 tree-sitter-yaml.workspace = true
 tree-sitter-bash.workspace = true
+tree-sitter-md.workspace = true
 unindent.workspace = true
 util = { workspace = true, features = ["test-support"] }
 workspace = { workspace = true, features = ["test-support"] }

crates/editor/src/editor_tests.rs πŸ”—

@@ -27498,6 +27498,65 @@ async fn test_paste_url_from_other_app_creates_markdown_link_over_selected_text(
     ));
 }
 
+#[gpui::test]
+async fn test_markdown_list_indent_with_multi_cursor(cx: &mut gpui::TestAppContext) {
+    init_test(cx, |_| {});
+
+    let markdown_language = languages::language("markdown", tree_sitter_md::LANGUAGE.into());
+    let mut cx = EditorTestContext::new(cx).await;
+
+    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+
+    cx.set_state(&indoc! {"
+        - [ ] Item 1
+            - [ ] Item 1.a
+        - [Λ‡] Item 2
+            - [Λ‡] Item 2.a
+            - [Λ‡] Item 2.b
+        "
+    });
+
+    cx.update_editor(|editor, window, cx| {
+        editor.handle_input("X", window, cx);
+    });
+
+    cx.assert_editor_state(indoc! {"
+        - [ ] Item 1
+            - [ ] Item 1.a
+        - [Xˇ] Item 2
+            - [Xˇ] Item 2.a
+            - [Xˇ] Item 2.b
+        "
+    });
+}
+
+#[gpui::test]
+async fn test_markdown_list_indent_with_newline(cx: &mut gpui::TestAppContext) {
+    init_test(cx, |_| {});
+
+    let markdown_language = languages::language("markdown", tree_sitter_md::LANGUAGE.into());
+    let mut cx = EditorTestContext::new(cx).await;
+
+    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+
+    cx.set_state(indoc! {"
+        - [x] list item
+          - [x] sub list itemˇ
+        "
+    });
+
+    cx.update_editor(|editor, window, cx| {
+        editor.newline(&Newline, window, cx);
+    });
+
+    cx.assert_editor_state(indoc! {"
+        - [x] list item
+          - [x] sub list item
+          Λ‡
+        "
+    });
+}
+
 #[gpui::test]
 async fn test_paste_url_from_zed_copy_creates_markdown_link_over_selected_text(
     cx: &mut gpui::TestAppContext,

crates/languages/src/markdown/config.toml πŸ”—

@@ -24,4 +24,5 @@ rewrap_prefixes = [
 auto_indent_on_paste = false
 auto_indent_using_last_non_empty_line = false
 tab_size = 2
+decrease_indent_pattern = "^\\s*$"
 prettier_parser_name = "markdown"